使用Karma為Angular組件撰寫單元測試案例


我想為你的Angular網頁應用程序編寫單元測試。加速生產時間並不能成為積累技術債的合理理由。以下是一些開始的有力理由:

  1. 單元測試幫助盡可能早地識別問題,尤其是當多個團隊在同一個代碼庫上工作,不經意地引入了錯誤。避免在深夜接到生產支持的電話是一個值得追求的目標。

  2. 測試使您能夠自信地重構代碼,確保您的應用程序繼續按預期運行。您可以將您的代碼分割為可管理的,可測試的單元,而不是處理一個龐大的系統。

  3. 您公司的政策可能要求某種程度的代碼覆蓋率,通常為80%或更高。

如果你是新手,你可能不知道如何開始,或者為什麼這個重要。幸運的是,Angular讓它變得很容易。開始,只需在您的項目目錄中運行以下命令:

npm run test

這將在本地主機的9876端口打開一個Chrome瀏覽器窗口。

Image 1

點擊”Debug”按鈕以啟動測試。

Image 2

此時,由於我們還沒有寫任何測試,所以不會有任何測試運行。但是,您可以開始撰寫測試用例以覆蓋特定的,獨立的代碼片段。例如,讓我們考慮一個login.component.ts檔案,其中包含一個login()方法,該方法將一個布爾標誌從假轉為真:

export class LoginComponent {
  isLogon = false

  login() {
    this.isLogon = true
  }
}

接下來,創建一個名為login.component.spec.ts的檔案以撰寫您的測試用例。如下所示撰寫您的第一個測試用例:

import { async, ComponentFixture, TestBed } from "@angular/core/testing"
import { LoginComponent } from "./login.component"

describe("LoginComponent", () => {
  let component: LoginComponent
  let fixture: ComponentFixture<LoginComponent>

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [LoginComponent],
    }).compileComponents()
  }))

  beforeEach(() => {
    fixture = TestBed.createComponent(LoginComponent)
    component = fixture.componentInstance
    fixture.detectChanges()
  })

  it("should be able to log on", () => {
    component.login()
    expect(component.isLogon).toBeTruthy()
  })
})

在您的describe()函式中,您將找到測試用例。每個案例都在其自己的it()函式中。這裡的目標是測試isLogon標誌在觸發login()方法後是否變為真。

Image 3

您的第一個測試用例應該通過!如果另一個開發者修改了您的代碼,您的測試將捕獲它:

Image 4

在現實世界的情境中,您可能會向服務器發送API呼叫。但是,在您的測試期間重要的是不要真的撥打API。您應該用stub數據模擬您的API呼叫。

例如,讓我們加強我們的LoginComponent以進行服務呼叫:

import { AuthenticationService } from "../../services/authentication.service"

export class LoginComponent {
  constructor(private authenticationService: AuthenticationService) {}

  isLogon = false

  login() {
    this.authenticationService.login().subscribe(
      data => {
        this.isLogon = true
      },
      error => {
        this.isLogon = false
      }
    )
  }
}

現在,您的測試將會失敗,因為AuthenticationService尚未注入我們的測試環境。我們可以如下所示修復這個問題:

import { async, ComponentFixture, TestBed } from "@angular/core/testing"
import { LoginComponent } from "./login.component"
import { AuthenticationService } from "../../services/authentication.service"
import { of } from "rxjs"

const stubData = {
  username: "testing",
}

class FakeAuthenticationService {
  login() {
    return of(stubData)
  }
}

describe("LoginComponent", () => {
  let component: LoginComponent
  let fixture: ComponentFixture<LoginComponent>
  const newFakeAuthenticationService = new FakeAuthenticationService()

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [LoginComponent],
      providers: [
        {
          provide: AuthenticationService,
          useValue: newFakeAuthenticationService,
        },
      ],
    }).compileComponents()
  }))

  beforeEach(() => {
    fixture = TestBed.createComponent(LoginComponent)
    component = fixture.componentInstance
    fixture.detectChanges()
  })

  it("should be able to log on", () => {
    component.login()
    expect(component.isLogon).toBeTruthy()
  })
})

您的測試用例現在應該通過!

Image 5

這個例子為了演示的目的而簡化,但是重要的要取的是,你不應該避免撰寫單元測試。