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
,但作用是忽略当前测试或者测试组。