iT邦幫忙

2022 iThome 鐵人賽

DAY 9
1
Modern Web

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

Day 9 - 單元測試 - 測試 Angular 元件 - 資料繫結測試

  • 分享至 

  • xImage
  •  

前言

上一篇針對頁面上固定顯示部份進行測試,但現實的需求總是複雜的,依應用程式取得的資料顯示是較常實作的需求,這一篇就來說明在這種情境下要如何撰寫測試程式。

範例程式

這篇的範例程式會使用到 ProductCardComponent 元件 (檔案名為 product-card/product-card.component.ts)。這個元件會以卡片的方式來顯示外層所傳入的產品資訊,並讓使用者可以查詢此產品的明細資料,以及把所要的產品加入購物車。

此元件在建構式注入了 ShoppingCartService 服務,為了讓後續說明更能夠聚焦,除了在測試模組中引用 Angular Material 的模組外,也要在 providers 屬性中加入所注入的服務。

beforeEach(async () => {
  await TestBed.configureTestingModule({
    imports: [
      NoopAnimationsModule,
      MatButtonModule,
      MatIconModule,
      MatCardModule,
      MatSnackBarModule,
    ],
    declarations: [ProductCardComponent],
    providers: [ShoppingCartService],
  }).compileComponents();

  fixture = TestBed.createComponent(ProductCardComponent);
  component = fixture.componentInstance;
  component.product = new Product();
  fixture.detectChanges();
});

觸發 Angular 變更檢測

在 Angular 應用程式中,我們會利用各種資料繫結的方式,來串連頁面與資料兩個部份,當資料變更時會觸發 Angular 的變更檢測來更新頁面。

在單元測試中,當我們更新元件資料時,並不會自動觸發 Angular 的變更檢測,因此我們需要利用 fixture.detectChanges() 方法來通知測試模組進行資料綁定。

it('當傳入產品 B 時, 產品名稱應顯示為"產品 B"', () => {
  // Arrange
  const product = new Product({ id: 2, name: '產品 B', price: 200 });
  const productTitleElement = fixture.debugElement.query(
    By.css('h3')
  ).nativeElement;
  const productPriceElement = fixture.debugElement.query(
    By.css('.mat-card-content > div')
  ).nativeElement;

  // Act
  component.product = product;
  fixture.detectChanges();

  // Assert
  expect(productTitleElement.textContent).toBe('產品 B');
  expect(productPriceElement.textContent).toBe('$200.00');
});

上面程式中,一開始準備了產品資料,以及利用 debugElementquery() 方法取得產品標題與價格的頁面元素。接下來將產品資料設定到 ProductCardComponent 元件的 product 屬性,此時就需要執行 fixture.detectChanges() 方法來手動觸發 changeDetector 作業。最後就可以針對產品的標題與單價的頁面顯示進行驗證。

加入購物車動作測試

接下來,也來為「加入購物車」這個動作加入測試程式。首先,先設定一產品資訊設定至元件屬性;另外,因為在驗證上需要檢查 ShoppingCartService 服務內的記錄是否有新增資料,所以,一併透過 TestBed.inject 方法取得此服務實作。

it('當將產品 C 新增至購物車時, 購物車服務應記錄 1 筆資料', () => {
  // Arrange
  const shoppingCartService = TestBed.inject(ShoppingCartService);
  const product = new Product({ id: 3, name: '產品 C', price: 10 });

  component.product = product;
  fixture.detectChanges();

  // Act

  // Assert
});

在 Jasmine 裡有兩種方式可以觸發 click 事件。其一,是利用 DebugElement 物件的 triggerEventHandler 方法,如下面程式,此方法第一個參數為事件名稱,而第二個則為事件參數。

button.triggerEventHandler('click', null);

另一種方法是利用 natvieElement 內的 click 方法。

button.nativeElement.click();

因此,我們可以利用 queryAll() 取得元件內的按鈕,並觸發按鈕的 click 事件,就可以針對 ShoppingCartService 服務記錄的內容進行驗證。

it('當將產品 C 新增至購物車時, 購物車服務應記錄 1 筆資料', () => {
  // Arrange
  const shoppingCartService = TestBed.inject(ShoppingCartService);

  const product = new Product({ id: 3, name: '產品 C', price: 10 });

  component.product = product;
  fixture.detectChanges();

  // Act
  const button = fixture.debugElement.queryAll(By.css('button'))[1];
  button.nativeElement.click();

  // Assert
  expect(shoppingCartService.items).toEqual([
    new ShoppingCartItem({
      id: 1,
      productId: 3,
      product: product,
      count: 1,
    }),
  ]);
});

執行測試程式

在撰寫完測試程式後,就可以執行 ng test 命令來確認測試結果。

https://ithelp.ithome.com.tw/upload/images/20220924/20109645QzQxnaFXH8.png

接下來

這一篇針對了動態頁面的情境進行測試程式的撰寫,完整的測試程式可以參考 GitHub 中。實務上,元件設定的資料常會透過 Angular 服務從後端服務取得,接下來就來說明如何建立假服務物件來撰寫單元測試。


上一篇
Day 8 - 單元測試 - 測試 Angular 元件 - 頁面顯示驗證
下一篇
Day 10 - 單元測試 - 測試 Angular 元件 - 建立相依的假服務
系列文
今天我想來在 Angular 應用程式上加上測試保護30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言