前言回顾
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。
相关资源
- iThelp 文章地址:
![94d9733c243c212bfc273bddeda8c519.png](https://i-blog.csdnimg.cn/blog_migrate/0419e7032dbe5bf679bf29bdebf22ec1.jpeg)
- Angular 测试:
![9be5432c171f97929d2c597e5ce66908.png](https://i-blog.csdnimg.cn/blog_migrate/adb2f8c963a637125585e8599a3d591d.png)
- NG-ZORRO 测试:
![1591f7a02cb06d1854606e6ae9c2dcb4.png](https://i-blog.csdnimg.cn/blog_migrate/4dc6cacaf88abbc5a26961b17ad3aae9.png)