iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 29
0
Modern Web

Angular 元件庫 NG-ZORRO 基礎入門系列 第 29

[Angular 元件庫 NG-ZORRO 基礎入門] Day 29 - 原始碼初窺: 測試

前言回顧

core 檔案中還有一個重要模組:testing,這個模組下封裝了很多測試需要的常用方法,測試 在 NG-ZORRO 專案中是非常重要的一部分,完善的測試程式碼可以最大程度地保證元件的可用性和程式碼質量。

測試

Angular 測試

背景知識

我們都知道,在我們建立 Angular 專案時,Angular 會自動幫我們在 package.json 裡安裝 Jasmine 測試框架karma 測試執行器

"devDependencies":  {
     "jasmine-core": "~3.4.0",
    "jasmine-spec-reporter": "~4.2.1",
    "karma": "~4.2.0",
    "karma-chrome-launcher": "~3.1.0",
    "karma-coverage-istanbul-reporter": "~2.1.0",
    "karma-jasmine": "~2.0.1",
    "karma-jasmine-html-reporter": "^1.4.2",
    "karma-spec-reporter": "0.0.32",
    "karma-viewport": "^1.0.4",
}

參考定義如下:

Jasmine:Jasmine是JavaScript的行為驅動開發測試框架。它不依賴瀏覽器,DOM或任何JavaScript框架。因此,它適用於網站,Node.js專案或JavaScript可以執行的任何地方。
Karma:Karma 是一個基於 Node.js 的 JavaScript 測試執行過程管理工具(Test Runner)。該工具可用於測試所有主流 Web 瀏覽器,也可以整合到 CI(Continuous integration)工具,還可以和其他程式碼編輯器一起使用。

基本結構

我們建立一個帶有測試的演示元件 TestComponent,然後開啟 test.component.spec.ts

import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { TestComponent } from './test.component';

describe('TestComponent', () => {
  // 每個測試用的必要準備
  let component: TestComponent;
  let fixture: ComponentFixture<TestComponent>;

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

  beforeEach(() => {
    fixture = TestBed.createComponent(TestComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });
  
  //測試用例
  it('should create', () => {
    expect(component).toBeTruthy();
  });
});

元件測試

還記得我們之前使用的 Table 元件嗎,我們挑選幾個 table 元件的測試用例來看一下它是如何工作的。

測試用例

beforeEach(async(() => {
  TestBed.configureTestingModule({
	imports: [...],
	declarations: [...]
  });
}));
describe('basic nz-table', () => {
  let fixture: ComponentFixture<NzTestTableBasicComponent>;
  let testComponent: NzTestTableBasicComponent;
  let table: DebugElement;

  beforeEach(() => {
	fixture = TestBed.createComponent(NzTestTableBasicComponent);
	fixture.detectChanges();
	testComponent = fixture.debugElement.componentInstance;
	table = fixture.debugElement.query(By.directive(NzTableComponent));
  });
  
  // 測試頁碼是否工作
  it('should pageIndex click work', () => {
	fixture.detectChanges();
	expect(testComponent.pageIndex).toBe(1);
	expect(testComponent.pageIndexChange).toHaveBeenCalledTimes(0);
	// 點選事件測試
	table.nativeElement.querySelectorAll('.ant-pagination-item')[1].click();
	fixture.detectChanges();
	expect(testComponent.pageIndex).toBe(2);
	expect(testComponent.pageIndexChange).toHaveBeenCalledTimes(1);
	expect(table.nativeElement.querySelector('.ant-pagination-item-active').innerText).toBe('2');
  });
});
...
export class NzTestTableBasicComponent implements OnInit {...}

我們可以看到這個用例測試了點選頁碼時,Pagination 是否能正常切換,通過 querySelectorAll 獲取到指定元素後執行點選事件,然後使用 expect 斷言測試是否達到預期。

之前說過,core/testing 資料夾封裝了很多事件,dispatchEvent 就是其中之一:

import { createFakeEvent, createKeyboardEvent, createMouseEvent, createTouchEvent } from './event-objects';
/** Utility to dispatch any event on a Node. */
export function dispatchEvent(node: Node | Window, event: Event): Event {
  node.dispatchEvent(event);
  return event;
}
/** Shorthand to dispatch a fake event on a specified node. */
export function dispatchFakeEvent(node: Node | Window, type: string, canBubble?: boolean): Event {
  return dispatchEvent(node, createFakeEvent(type, canBubble));
}
/** Shorthand to dispatch a keyboard event with a specified key code. */
export function dispatchKeyboardEvent(node: Node, type: string, keyCode: number, target?: Element): KeyboardEvent {
  return dispatchEvent(node, createKeyboardEvent(type, keyCode, target)) as KeyboardEvent;
}
/** Shorthand to dispatch a mouse event on the specified coordinates. */
export function dispatchMouseEvent(
  node: Node,
  type: string,
  x: number = 0,
  y: number = 0,
  event: MouseEvent = createMouseEvent(type, x, y)
): MouseEvent {
  return dispatchEvent(node, event) as MouseEvent;
}
/** Shorthand to dispatch a touch event on the specified coordinates. */
export function dispatchTouchEvent(node: Node, type: string, x: number = 0, y: number = 0): TouchEvent {
  return dispatchEvent(node, createTouchEvent(type, x, y)) as TouchEvent;
}

我們來用 dispatchMouseEvent 滑鼠事件方法來替換 click() 事件。

const paginationNode = table.nativeElement.querySelectorAll('.ant-pagination-item')[1];
dispatchMouseEvent(paginationNode, 'click');

同樣可以達到滑鼠點選的效果。

Service

如果有元件需要引入 service 的話,別忘了在 providers 引入:

// example
beforeEach(async(() => {
  TestBed.configureTestingModule({
	imports: [...],
	declarations: [...],
	providers: [XxxService]
  });
}));

測試目的

測試的目的主要是對已知元件屬性的全面測試,保證元件在釋出前滿足設計期望。當然,這個過程中必然可能存在無法預測的場景,如果遇到問題還是得依靠社群使用者來反饋。

總結 & 預告

我們今天介紹了 NG-ZORRO 專案的測試相關功能,這也是 NG-ZORRO 元件覆蓋率和質量的保證,所以任何修改(尤其是新特性)都需要增加測試用例,低於預期覆蓋率的 PR 是不會通過 CI 的。

明天我們將介紹如何參與社群貢獻,如何去提交程式碼成為社群貢獻者,這也是我們這個系列的最後一篇文章。到此為止,我們已經介紹了大部分 NG-ZORRO 元件相關的知識,更多的內容仍然需要大家自行去學習瞭解,畢竟只靠文字是無法涉及到一切的。如果有時間,我們也會在後面重新整理相關程式碼和文件同步上傳至 github。

相關資源


上一篇
[Angular 元件庫 NG-ZORRO 基礎入門] Day 28 - 原始碼初窺: core
下一篇
[Angular 元件庫 NG-ZORRO 基礎入門] Day 30 - 社群貢獻
系列文
Angular 元件庫 NG-ZORRO 基礎入門30

尚未有邦友留言

立即登入留言