iT邦幫忙

2022 iThome 鐵人賽

DAY 15
2
Modern Web

今天我想來在 Angular 應用程式上加上測試保護系列 第 15

Day 15 - 單元測試 - 測試 Angular 元件 - 使用 Page 物件

  • 分享至 

  • xImage
  •  

前言

在之前的篇幅中,針對 Angular 元件的測試常會去檢查頁面上的顯示是否符合預期,因此常會需要在各個測試案例中使用 query 來找尋特定的頁面元素。然而,在元件有較為複雜的頁面時,就會讓找尋頁面元素的動作散落在測試檔案的每個地方,增加測試程式的維護成本。這一篇則會說明如何定義一 Page 類別來控制頁面元素的查詢。

範例程式

這一篇會重構 ProductCardComponent 元件的測試程式,把測試程式內所使用的頁面元素,都封裝進 Page 類別中。

定義 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 類別內的定義,而不需要動到單元測試程式,大大減少維護的成本。

執行測試程式

https://ithelp.ithome.com.tw/upload/images/20220930/20109645YHnHtnrMbc.png

接下來

這一篇說明了如何利用 Page 類別的定義來重構測試程式,完整的測試程式可以參考 GitHub。接下來,就是 Jasmine 單元測試的最後一篇,來說明 Karma 的組態設定與操作。


上一篇
Day 14 - 單元測試 - 測試 Angular 元件 - 路由測試
下一篇
Day 16 - 單元測試 - Karma 組態設定與使用
系列文
今天我想來在 Angular 應用程式上加上測試保護30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言