core
檔案中還有一個重要模組:testing
,這個模組下封裝了很多測試需要的常用方法,測試
在 NG-ZORRO 專案中是非常重要的一部分,完善的測試程式碼可以最大程度地保證元件的可用性和程式碼質量。
我們都知道,在我們建立 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
的話,別忘了在 providers
引入:
// example
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [...],
declarations: [...],
providers: [XxxService]
});
}));
測試的目的主要是對已知元件屬性的全面測試,保證元件在釋出前滿足設計期望。當然,這個過程中必然可能存在無法預測的場景,如果遇到問題還是得依靠社群使用者來反饋。
我們今天介紹了 NG-ZORRO 專案的測試相關功能,這也是 NG-ZORRO 元件覆蓋率和質量的保證,所以任何修改(尤其是新特性)都需要增加測試用例,低於預期覆蓋率的 PR
是不會通過 CI
的。
明天我們將介紹如何參與社群貢獻,如何去提交程式碼成為社群貢獻者,這也是我們這個系列的最後一篇文章。到此為止,我們已經介紹了大部分 NG-ZORRO 元件相關的知識,更多的內容仍然需要大家自行去學習瞭解,畢竟只靠文字是無法涉及到一切的。如果有時間,我們也會在後面重新整理相關程式碼和文件同步上傳至 github。