如下是我假定那些极少或压根没写单元测试的人准备的,所以,会白话解释诸多概念性问题,同时会结合 Jasmine 与之对应的方法进行讲解。typescript
测试套件,哪怕一个简单的类,也会有若干的测试用例,所以将这些测试用例集合在一个分类下就叫Test Suite。segmentfault
而在 Jasmine 就是使用 describe
全局函数来表示,它的第一个字符串参数用来表示Suite的名称或标题,第二个方法参数就是实现Suite代码了。app
describe('test suite name', () => { });
一个Specs至关于一个测试用例,也就是咱们实现测试具体代码体。异步
Jasmine 就是使用 it
全局函数来表示,和 describe
相似,字符串和方法两个参数。async
而每一个 Spec 内包括多个 expectation 来测试须要测试的代码,只要任何一个 expectation 结果为 false
就表示该测试用例为失败状态。函数
describe('demo test', () => { const VALUE = true; it('should be true', () => { expect(VALUE).toBe(VALUE); }) });
断言,使用 expect
全局函数来表示,只接收一个表明要测试的实际值,而且须要与 Matcher 表明指望值。工具
断言匹配操做,在实际值与指望值之间进行比较,并将结果通知Jasmine,最终Jasmine会判断此 Spec 成功仍是失败。单元测试
Jasmine 提供很是丰富的API,一些经常使用的Matchers:测试
toBe()
等同 ===
!==
!== undefined
=== undefined
=== null
!!obj
!obj
<
>
==
!=
indexOf
new RegExp().test()
!new RegExp().test()
而这些API以前用 not
来表示负值的判断。ui
expect(true).not.toBe(false);
这些Matchers几乎能够知足咱们平常需求,固然你也能够定制本身的Matcher来实现特殊需求。
一份干将的测试代码很重要,所以咱们能够将这些重复的 setup 与 teardown 代码,放在与之相对应的 beforeEach
与 afterEach
全局函数里面。
beforeEach
表示每一个 Spec 执行以前,反之。
describe('demo test', () => { let val: number = 0; beforeEach(() => { val = 1; }); it('should be true', () => { expect(val).toBe(1); }); it('should be false', () => { expect(val).not.toBe(0); }); });
如同上面示例中,咱们能够在每一个测试文件开头、describe
来定义相应的变量,这样每一个 it
内部能够共享它们。
固然,每一个 Spec 的执行周期间也会伴随着一个空的 this
对象,直至 Spec 执行结束后被清空,利用 this
也能够作数据共享。
有时候当咱们对某个组件进行测试时,而这个组件会有不一样状态来展现不一样的结果,这个时候若是只用一个 describe
会显得不过优雅。
所以,嵌套 describe
,会让测试代码、测试报告看起来更漂亮。
describe('AppComponent', () => { describe('Show User', () => { it('should be show panel.', () => {}); it('should be show avatar.', () => {}); }); describe('Hidden User', () => { it('should be hidden panel.', () => {}); }); });
需求老是三心二意的,但好不容易写好的测试代码,难道要删除吗?非也……
Suites 和 Specs 分别能够用 xdescribe
和 xit
全局函数来跳过这些测试代码块。
Angular的自定义事件实在太广泛了,但为了测试这些自定义事件,所以监控事件是否正常被调用是很是重要。好在,Spy
能够用于监测函数是否被调用,这简直就是咱们的好伙伴。
如下示例暂时无须理会,暂且体验一下:
describe('AppComponent', () => { let fixture: ComponentFixture<TestComponent>; let context: TestComponent; beforeEach(() => { TestBed.configureTestingModule({ declarations: [TestComponent] }); fixture = TestBed.createComponent(TestComponent); context = fixture.componentInstance; // 监听onSelected方法 spyOn(context, 'onSelected'); fixture.detectChanges(); }); it('should be called [selected] event.', () => { // 触发selected操做 // 断言是否被调用过 expect(context.onSelected).toHaveBeenCalled(); }); });
首先,这里的异步是指带有 Observable 或 Promise 的异步行为,所以对于组件在调用某个 Service 来异步获取数据时的测试状态。
假设咱们的待测试组件代码:
export class AppComponent { constructor(private _user: UserService) {} query() { this._user.quer().subscribe(() => {}); } }
async
async
无任何参数与返回值,全部包裹代码块里的测试代码,能够经过调用 whenStable()
让全部待处理异步行为都完成后再进行回调;最后,再进行断言操做。
it('should be get user list (async)', async(() => { // call component.query(); fixture.whenStable().then(() => { fixture.detectChanges(); expect(true).toBe(true); }); }));
fakeAsync
若是说 async
还须要回调才能进行断点让你受不了的话,那么 fakeAsync
能够解决这一点。
it('should be get user list (async)', fakeAsync(() => { // call component.query(); tick(); fixture.detectChanges(); expect(true).toBe(true); }));
这里只是将回调换成 tick()
,怎么样,是否是很酷。
Jasmine自带异步
如前面所说的异步是指带有 Observable 或 Promise 的异步行为,而有时候咱们有些东西是依赖 setTimeout
或者多是须要外部订阅结果之后才能触发时怎么办呢?
可使用 done()
方法。
it('async demo', (done: () => void) => { context.show().subscribe(res => { expect(true).toBe(true); done(); }); el.querySelected('xxx').click(); });
本章几乎全部的内容在Angular单元测试常用到的东西;特别是异步部分,三种不一样异步方式并不是共存的,而是须要根据具体业务而采用。不然,你会发现真TM难写单元测试。毕竟这是一个异步的世界。
自此,咱们算是为Angular写单元测试打下了基础。后续,将不会再对这类基础进行解释。
那么下一篇,咱们将介绍Component、Directive、Pipe 以及Service单元测试。
happy coding!