在之前的篇幅中,針對 Angular 元件的測試常會去檢查頁面上的顯示是否符合預期,因此常會需要在各個測試案例中使用 query
來找尋特定的頁面元素。然而,在元件有較為複雜的頁面時,就會讓找尋頁面元素的動作散落在測試檔案的每個地方,增加測試程式的維護成本。這一篇則會說明如何定義一 Page 類別來控制頁面元素的查詢。
這一篇會重構 ProductCardComponent
元件的測試程式,把測試程式內所使用的頁面元素,都封裝進 Page 類別中。
一開始在此元件資料夾內,新增一 product-card.component.po.ts
檔案,用來定義頁面上會使用到的頁面元素。
import { DebugElement } from '@angular/core';
import { By } from '@angular/platform-browser';
export class Page {
get title(): HTMLElement {
return <HTMLElement>this.debugElement.query(By.css('h3')).nativeElement;
}
get price(): HTMLElement {
return <HTMLElement>(
this.debugElement.query(By.css('.mat-card-content > div')).nativeElement
);
}
get buttons(): DebugElement[] {
return this.debugElement.queryAll(By.css('button'));
}
get searchButton(): HTMLButtonElement {
return <HTMLButtonElement>this.buttons[0].nativeElement;
}
get addButton(): HTMLButtonElement {
return <HTMLButtonElement>this.buttons[1].nativeElement;
}
constructor(private debugElement: DebugElement) {}
}
如上面程式,此 Page 類別的建構式會傳入 DebugElement
物件,並且利用 getter
的方式定義如標題、單價與按鈕等頁面元素。
接下來,在測試程式中利用 fixture.debugElement
建立產品卡片元件的 Page 物件。
describe('ProductCardComponent', () => {
let component: ProductCardComponent;
let fixture: ComponentFixture<ProductCardComponent>;
let page: Page;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ ... ],
declarations: [ProductCardComponent],
providers: [ShoppingCartService],
}).compileComponents();
fixture = TestBed.createComponent(ProductCardComponent);
page = new Page(fixture.debugElement);
component = fixture.componentInstance;
});
});
然後,就可以在把測試案例中的頁面元素都修改使用成此 Page 物件的屬性。
it('當傳入產品 B 時, 產品名稱應顯示為"產品 B"', () => {
// Arrange
const product = new Product({ id: 2, name: '產品 B', price: 200 });
// Act
component.product = product;
fixture.detectChanges();
// Assert
expect(page.title.textContent).toBe('產品 B');
expect(page.price.textContent).toBe('$200.00');
});
it('當將產品 C 新增至購物車時, 購物車服務應記錄 1 筆資料', () => {
// Arrange
const shoppingCartService = TestBed.inject(ShoppingCartService);
spyOn(shoppingCartService, 'add').and.callThrough();
const product = new Product({ id: 3, name: '產品 C', price: 10 });
component.product = product;
fixture.detectChanges();
// Act
page.addButton.click();
// Assert
expect(shoppingCartService.add).toHaveBeenCalledWith(product);
如以一來,當頁面的結構變更的時候,就可以只修改 Page 類別內的定義,而不需要動到單元測試程式,大大減少維護的成本。
這一篇說明了如何利用 Page 類別的定義來重構測試程式,完整的測試程式可以參考 GitHub。接下來,就是 Jasmine 單元測試的最後一篇,來說明 Karma 的組態設定與操作。