利用Jasmine对Angular进行单元测试的方法详解
前言
本文主要介绍的是关于利用jasmine对angular单元测试的相关内容,以下是我假定那些极少或压根没写单元测试的人准备的,因此,会白话解释诸多概念性问题,同时会结合 jasmine 与之对应的方法进行讲解。
一、概念
test suite
测试套件,哪怕一个简单的类,也会有若干的测试用例,因此将这些测试用例集合在一个分类下就叫test suite。
而在 jasmine 就是使用 describe 全局函数来表示,它的第一个字符串参数用来表示suite的名称或标题,第二个方法参数就是实现suite代码了。
describe('test suite name', () => { });
specs
一个specs相当于一个测试用例,也就是我们实现测试具体代码体。
jasmine 就是使用 it 全局函数来表示,和 describe 类似,字符串和方法两个参数。
而每个 spec 内包括多个 expectation 来测试需要测试的代码,只要任何一个 expectation 结果为 false 就表示该测试用例为失败状态。
describe('demo test', () => { const value = true; it('should be true', () => { expect(value).tobe(value); }) });
expectations
断言,使用 expect 全局函数来表示,只接收一个代表要测试的实际值,并且需要与 matcher 代表期望值。
二、常用方法
matchers
断言匹配操作,在实际值与期望值之间进行比较,并将结果通知jasmine,最终jasmine会判断此 spec 成功还是失败。
jasmine 提供非常丰富的api,一些常用的matchers:
- tobe() 等同 ===
- tonotbe() 等同 !==
- tobedefined() 等同 !== undefined
- tobeundefined() 等同 === undefined
- tobenull() 等同 === null
- tobetruthy() 等同 !!obj
- tobefalsy() 等同 !obj
- tobelessthan() 等同 <
- tobegreaterthan() 等同 >
- toequal() 相当于 ==
- tonotequal() 相当于 !=
- tocontain() 相当于 indexof
- tobecloseto() 数值比较时定义精度,先四舍五入后再比较。
- tohavebeencalled() 检查function是否被调用过
- tohavebeencalledwith() 检查传入参数是否被作为参数调用过
- tomatch() 等同 new regexp().test()
- tonotmatch() 等同 !new regexp().test()
- tothrow() 检查function是否会抛出一个错误
而这些api之前用 not 来表示负值的判断。
expect(true).not.tobe(false);
这些matchers几乎可以满足我们日常需求,当然你也可以定制自己的matcher来实现特殊需求。
setup 与 teardown
一份干将的测试代码很重要,因此我们可以将这些重复的 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
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写单元测试打下了基础。后续,将不会再对这类基础进行解释。
之后我们将介绍组件与指令单元测试。
好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如有疑问大家可以留言交流,谢谢大家对的支持。
上一篇: 我和他儿媳相好
推荐阅读
-
在Pycharm中对代码进行注释和缩进的方法详解
-
Linux中利用Vim对文件进行密码保护的方法详解
-
在python中利用GDAL对tif文件进行读写的方法
-
利用Distinct()内置方法对List集合的去重问题详解
-
.NET Core利用swagger进行API接口文档管理的方法详解
-
使用Angular CLI进行单元测试和E2E测试的方法
-
对Python的Django框架中的项目进行单元测试的方法
-
SpringMVC中利用@InitBinder来对页面数据进行解析绑定的方法
-
在python中利用KNN实现对iris进行分类的方法
-
Python 利用内置set函数对字符串和列表进行去重的方法