jest是什么
进入正题前先讲一个老笑话:
一群软件项目经理参加一个培训。培训老师提了个问题:“假设今天你手头的项目完成了,恰好这个项目的内容是制造一架飞机,那么,你愿意参加首飞吗?愿意的请举手。”
台下只有两位举起了手,培训老师把那两位项目经理请上了讲台,然后问他们为什么敢于举手。
第一位说:“我们的管理很严格,规范也很全面,我相信我的团队100%完成了任务,我有信心”。全场都向他投去了钦佩羡慕的目光。
轮到第二位了,他说:“和他相反,我坚信我们的飞机根本飞不起来,所以我能很放心的参加首飞。”
尽管这是一个笑话,但也说明开发完成完全不能等同于交付生产。其中测试是必不可少的环节。
说到软件测试,往往想到的是软件测试人员在软件开发完成后,对软件产品的测试。其实在开发阶段,开发人员就可以进行单元测试和集成测试了。
单元测试和集成测试,可以把代码缺陷消灭在萌芽阶段,避免存在问题的代码进入后面的环节,降低成本。
jest就是可以用于这两方面的JavaScript测试框架,Facebook出品。
jest拥有比较全面的断言库, 支持同步异步代码测试,能提供测试覆盖率报告,对于某些不便直接测试的功能,还可以提供Mock支持。
jset如何使用
因为jset是一个npm包,这里按照通常情形:在自己的npm项目中使用jset。其他使用情形不讨论。
安装
使用npm或者yarn安装jset包即可。
1 | yarn add --dev jest |
或者
1 | npm install --save-dev jest |
官方推荐使用yarn。
配置
定制生成jest配置文件
运行如下命令:
1 | npx jest --init |
命令运行后,会有一系列问答,根据需求设置就好。完成后会在项目目录中生成名为jest.conifg.js的文件,它是一个node模块,定义了jest的配置信息,具体内容在文件中有注释,可以查看。
配置自定义npm命令
为了方便调用jest命令,在package.json文件中添加如下的命令脚本:
1 | "scripts": { |
配置完成后,运行npm run test就可以进行jset测试,运行 npm run converage就可以进行jset测试同时生成测试报告。
如果想一边写测试代码,一边看结果,不妨做如下设置:
1 | "test": "jest ----watchAll", |
运行这个命令后jest测试完成后,不会返回终端,而是等待测试文件的修改,一旦发现测试文件修改,就立即执行测试。
配置支持ES6语法
jset并不天然支持ES6,所以需要使用babel进行转换,详见文档。其中也能找到支持TypeScript的配置方法。
使用
根据官方文档,可以把jest和webpack结合使用,作为入门文章这里只讲使用前面配置好的npm run test和npm run converage命令执行测试代码的情形。
jest测试代码编写
测试文件命名
jest测试代码也是js文件,只需要以xxx.test.js这样的格式命名即可。不是以此格式命名的文件会被忽略。
相关语句语法
expect语句能够将值和匹配器做比较,从而测试程序是否符合预期;test(别名是it)语句包裹一个测试;describe语句包裹一系列相关测试;
测试文件组织
一个文件可以由一个或者多个
describe语句组成,用来测试一组或者多组测试,如果已经通过测试文件分离了多组测试,那么可以没有describe语句;一个
describe语句通常会包含一个或者多个test语句,describe还支持嵌套;一个
test语句通常也会包含一个或者多个expect语句
测试文件样例
这样下来,一个测试文件大概长成这个样子:
1 | describe('Test String', () => { |
PS:
以上例子中,expect语句中都直接给定了值,实际情况肯定不是如此,应该在测试文件中引用相关文件的相关方法,调用得到值,然后再和匹配器做比较。
匹配器
jest有一大堆让人眼花缭乱的匹配器,他们都在这里,有些让人生畏,但同时也是好事,找到合适的直接拿来用就行了。
常用匹配器
| 名称 | 等价方法 | 说明 |
|---|---|---|
| toBe | 对于基本类型,比较值(Object.is);对于引用类型比较是否为同一个引用 | |
| toEqual | 对于引用类型,深度(递归)比较,基本类型比较值(Object.is) | |
| toBeDefined | something!=undefined | 检查变量已经定义 |
| toBeFalsy | 检查内容是否为false, 0, '', null, undefined, 或者 NaN |
|
| toBeGreaterThan | > | |
| toBeGreaterThanOrEqual | >= | |
| toBeLessThan | < | |
| toBeLessThanOrEqual | <= | |
| toBeNull | 检查预期值是null | |
| toBeTruthy | 和toBeFalsy相反 | |
| toBeUndefined | 检查预期值是undefined | |
| toBeNaN | 检查预期值是NaN | |
| toContain | 内容是否在数组中(===);字符串是否包含在另一个字符串中 | |
| toContainEqual | 检查内容是否在数组中,会进行递归深度比较 | |
| toMatch | 检查字符串是否符合特定规则 | |
| toMatchObject | 检查对象是否符合部分给定的特征 | |
| toStrictEqual | 比较内容和特定的结构是否相符;可以比较对象、数组、class实例和字面量对象,即使有相同的属性名称和值,也不能通过 | |
| toThrow(error?) | 检查抛出了异常 |
异步测试
异步代码是JavaScript中常有的,异步测试和同步测试有很大的的区别,需要特别注意。
实现异步编程,通常有采用回调方法、Promise和async/await三类,下面分别介绍这三种情况的测试。另外异步方法也可能抛出异常,我们也会介绍具体的测试方法。
测试回调方法
在测试的回调方法中一定要调用done方法。
done方法是jest自身提供的,需要在回调方法中的各种情况下都调用一次,这样在done之前的测试代码才会执行。比如如下代码:
1 | test('在回调方法中调用done,保证回调方法被执行,结果被验证', (done) => { |
测试promise
记得要return
test中的测试方法一定要返回promise,否则测试代码无法执行。
1 | test('promise 测试,正确例子,一定要返回', () => { |
采用expect.assertions() 测试异常
如果直接在primise对象的catch方法中测试,会漏掉测试方法正常执行没有被catch捕获的情况。所以需要在测试代码之前断言expect至少被执行一次:
1 | test('promise测试,测试异常情况,正确例子',()=>{ |
如果一个test只有一个测试内容,也可以使用 expect.hasAssertions()。
.resolves/.rejects
可以使用.resolves/.rejects指定测试promise的返回结果。参见文档
Async/Await
测试代码需要声明为async方法,方法体内使用await等待返回。
1 | test('async await',async ()=>{ |
也可以进一步和.resolves/.rejects组合。
钩子函数
- beforeAll
- afterAll
- beforeEach
- afterEach
分别在所欲测试开始前/后执行,每一项测试开始前/后执行。可以在这些钩子函数中做测试前的准备,测试后的善后。
测试分组和钩子函数
测试分组和钩子函数可以结合使用。
- 在外面定义的钩子函数对内层有效
- 层内定义的钩子函数支队本层以及子层有效。
- 外层的
before*钩子先执行 beforeAll先于beforeEach执行- 外层的
after*钩子后执行 afterEach先于afterAll执行
开始:先执行外层beforeAll,后执行内层beforeAll ,直至没有内层
开始执行一条测试:先执行外层beforeEach,后执行内层beforeEach,直至没有内层;
一条测试执行完成:先执行内层afterEach,后执行外层afterEach,直至没有外层;
结束:先执行内层afterAll,后执行外层afterAll,直至没有外层
其他
only
可以给describe和test添加only,这样没有添加only的测试组或者测试会被忽略。
skip
类似 only,但作用是忽略当前测试或者测试组。