利用Jasmine对Angular进行单元测试的方法详解


Posted in Javascript onJune 12, 2017

前言

本文主要介绍的是关于利用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写单元测试打下了基础。后续,将不会再对这类基础进行解释。

之后我们将介绍组件与指令单元测试。

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
javascript 进阶篇3 Ajax 、JSON、 Prototype介绍
Mar 14 Javascript
js转义字符介绍
Nov 05 Javascript
基于JQuery实现的图片自动进行缩放和裁剪处理
Jan 31 Javascript
不能不知道的10个angularjs英文学习网站
Mar 23 Javascript
巧方法 JavaScript获取超链接的绝对URL地址
Jun 14 Javascript
jQuery Ajax 上传文件处理方式介绍(推荐)
Jun 30 Javascript
js完整倒计时代码分享
Sep 18 Javascript
ui-router中使用ocLazyLoad和resolve的具体方法
Oct 18 Javascript
详解JavaScript 中 if / if...else...替换方式
Jul 15 Javascript
vuex actions传递多参数的处理方法
Sep 18 Javascript
详解JQuery基础动画操作
Apr 12 jQuery
vue 实现动态路由的方法
Jul 06 Javascript
原JS实现banner图的常用功能
Jun 12 #Javascript
手把手搭建安装基于windows的Vue.js运行环境
Jun 12 #Javascript
JS获取鼠标坐标并且根据鼠标位置不同弹出不同内容
Jun 12 #Javascript
jQuery Json数据格式排版高亮插件json-viewer.js使用方法详解
Jun 12 #jQuery
ionic中的$ionicPlatform.ready事件中的通用设置
Jun 11 #Javascript
JS判断一个数是否是水仙花数
Jun 11 #Javascript
在bootstrap中实现轮播图实例代码
Jun 11 #Javascript
You might like
Apache 配置详解(最好的APACHE配置教程)
2010/07/04 PHP
PHP中SSO Cookie登录分析和实现
2015/11/06 PHP
PHP CURL或file_get_contents获取网页标题的代码及两者效率的稳定性问题
2015/11/30 PHP
Ubuntu 16.04中Laravel5.4升级到5.6的步骤
2018/12/07 PHP
如何在PHP环境中使用ProtoBuf数据格式
2020/06/19 PHP
combox改进版 页面原型参考dojo的,比网上jQuery的那些combox功能强,代码更小
2010/04/15 Javascript
基于jQuery的公告无限循环滚动实现代码
2012/05/11 Javascript
Javascript实现带关闭按钮的网页漂浮广告代码
2014/01/12 Javascript
用js设置下拉框为只读的小技巧
2014/04/10 Javascript
使用cluster 将自己的Node服务器扩展为多线程服务器
2014/11/10 Javascript
基于JavaScript代码实现微信扫一扫下载APP
2015/12/30 Javascript
jQuery实现返回顶部功能
2016/02/23 Javascript
快速解决jquery.touchSwipe左右滑动和垂直滚动条冲突
2016/04/15 Javascript
js+html5实现canvas绘制网页时钟的方法
2016/05/21 Javascript
NPM 安装cordova时警告:npm WARN deprecated minimatch@2.0.10: Please update to minimatch 3.0.2 or higher to
2016/12/20 Javascript
解决拦截器对ajax请求的拦截实例详解
2016/12/21 Javascript
jquery 禁止鼠标右键并监听右键事件
2017/04/27 jQuery
使用webpack搭建react开发环境的方法
2018/05/15 Javascript
解决layui富文本编辑器图片上传无法回显的问题
2019/09/18 Javascript
python数据库操作常用功能使用详解(创建表/插入数据/获取数据)
2013/12/06 Python
python爬虫正则表达式之处理换行符
2018/06/08 Python
Python实现读取SQLServer数据并插入到MongoDB数据库的方法示例
2018/06/09 Python
python 实现查找文件并输出满足某一条件的数据项方法
2019/06/12 Python
python函数调用,循环,列表复制实例
2020/05/03 Python
HTML5实践-图片设置成灰度图
2012/11/12 HTML / CSS
h5调用摄像头的实现方法
2016/06/01 HTML / CSS
HTML5通用接口详解
2016/06/12 HTML / CSS
有机童装:Toby Tiger
2018/05/23 全球购物
德国汽车零件和汽车配件网上商店:kfzteile24
2018/11/14 全球购物
孝老爱亲模范事迹
2014/01/24 职场文书
财产公证书
2014/04/10 职场文书
乡镇爱国卫生月活动总结
2014/06/25 职场文书
2014入党积极分子破除“四风”思想汇报
2014/09/14 职场文书
企业财务人员岗位职责
2015/04/14 职场文书
导游词之日本富士山
2020/01/06 职场文书
关于mysql中string和number的转换问题
2022/06/14 MySQL