Jest 是由 Facebook 开发并维护的一套 js 的单元测试框架,之前在后台的 nodejs 项目里面第一次尝试使用,感觉还是非常容易上手的,功能也比较强大。尤其是 mock 方面也别好用,还天然的支持覆盖率,所以非常推荐使用。
内置支持的功能如下:
灵活的配置:比如,可以用文件名通配符来检测测试文件;
测试的事前步骤 (Setup) 和事后步骤 (Teardown),同时也包括测试范围;
匹配表达式 (Matchers):能使用期望 expect 句法来验证不同的内容;
测试异步代码:支持承诺 (promise) 数据类型和异步等待 async / await 功能;
模拟函数:可以修改或监查某个函数的行为;
手动模拟:测试代码时可以忽略模块的依存关系;
虚拟计时:帮助控制时间推移。
将下面的配置部分添加到你的 package.json 里面:
"scripts": {
"test": "jest"
}
执行下面的命令即可:
npm test
注意:jest 会自动搜索路径下面所有 test.js 结尾的文件, 默认都会执行。
如果想单独运行某个测试文件可以直接加上文件名就可以。
如上面的例子可以:
npm test sum.test.js 或者 jest sum.test.js
也可以在 jest 配置文件里配置上 testRegex
testRegex 默认值:(/tests/.*|(\.|/)(test|spec))\.jsx?$
可以参考:
https://facebook.github.io/jest/docs/en/configuration.htmlcollectCoverage里面,比较有用的就是 默认是 false,设置成 true 的话执行完测试就会自动统计覆盖率。
除了用 npm test 执行测试,也可以直接 jest 执行所有用例,jest 支持的命令行参数可以参考:
https://facebook.github.io/jest/docs/zh-Hans/cli.html
命令行参数仅支持 jest 执行,npm test 这样是不支持命令行的。
下面介绍比较常用的:
(1)——runInBand
jest --runInBand
可以顺序执行所有用例,默认所有用例是并行执行的。
(2)——debug
执行前打印 jest 所有配置信息。
2.1.1 Mock 一个函数
方法的 mock 非常简单,使用 jest.fn 就可以非常简单的 mock 一个函数。
如下面的例子:代码里面有一个函数叫 forEach。
此函数可以简单使用下面方法 mock,并且 jest 提供一些方法可以确保查看 mock 函数被调用的情况:
mock 属性的所有 api 可以参考:https://facebook.github.io/jest/docs/en/mock-function-api.html
2.1.2 Mock 返回值
可以使用 mock 注入返回值,可以使用的 api 为 mockReturnValue,mockReturnValueOnce 等。
2.1.3 Mock 内部实现
使用 jest.fn 或者 mockImplementationOnce 可以完全替换需要 mock 的函数。
当需要 mock 的函数是从其他模块创建的就可以使用 mockImplementation。
2.1.4 Mock 名字
可以使用 mockName 来给 mock 函数命名,如果没有命名,输出的日志默认就会打印 jest.fn(),加上名字更有利于调试。
另外有用的 mock 方法可以参考:
https://facebook.github.io/jest/docs/en/mock-functions.html
这里面有几种方式来 mock:
2.2.2 jest.mock() 直接在单元测试里面 mock 模块
例如我们很多产品代码里面会使用 fs 文件读取文件, 在单元测试中, 我们并不需要真去调用 fs 读取文件, 就可以考虑把 fs 模块 mock 掉, 如下代码:
2.2.3 在需要 mock 的模块目录临近建立目录mocks
这里面分两种情况:
2.2.3.1 对于用户目录下面的模块
例如我们需要 mock 目录 models 下面的 user 模块,那么我们就需要在 models 下面新建mocks目录(这里要区分大小写),然后新建文件 user.js。
注意:用这种方式, 需要在单元测试文件中需添加下面的代码才能使此 mock 生效。
2.2.3.2 对于 node_modules 下面的模块
如果我们需要 mock 的模块是一个 Node 的模块(如 lodash
),那么 mocks应该是挨着 node_modules 目录(除非你手动配置的 roots 指向非本项目的 root 目录),这种就会自动 mock 了,也就是不需要在单元测试用例里再调用 jest.mock('module_name')。
如果需要 mock 的模块是 scoped 模块,那么我们创建的 mock 的名字需要一致,例如, mock 模块名字为 @scope/project-name,那么就需要创建mocks/@scope/project-name.js。
注意:如果我们需要 mock node 的核心模块(如 fs 或者 path),那么还是需要显示的调用 jest.mock('path') , 因为核心的 node 模块默然是不被 mock 的。
类可以用四种方式来 mock 一个类。
2.3.1 jest.mock 自动 mock 类所在的模块, 类和类的方法也自动被 mock。
2.3.3 使用带模块工厂参数的 mock。
形式如下 jest.mock(path, moduleFactory),其中模板工厂参数指的是一个返回模块的函数
2.3.4. 使用 mockImplementation() 或者 mockImplementationOnce() 代替 mock
可以使用 mockImplementation() (or mockImplementationOnce()) 代替上面的带模板工厂参数的 mock 方法,mockImplementation 或者 mockImplementationOnce 来修改 mock。
可以参考:
https://facebook.github.io/jest/docs/en/es6-class-mocks.html
对于简单的函数的 mock,推荐使用 jest.fn 来进行 mock,针对不同的情况(例如返回值或者替换实现),可以考虑使用 mockReturnValue 和 mockImplementation;针对类和模块的 mock,推荐使用自动的 mock 方法也就是 jest.mock。对于比较复杂的类和接口,如果自动 mock 不能完成覆盖到的话,建议结合使用 jest.mock 和 jest.fn().mockImplementation,或者可以使用 jest.mock 完全自己 mock。
另外,jest 里面有 timer 的 mock,使用 jest.useFakeTimers() 可以自动 mock 代码里面的 setTimeout 和 setInterval 等函数具体信息请参考:
https://facebook.github.io/jest/docs/en/timer-mocks.html。
关注腾讯移动品质中心 TMQ,获取更多测试干货!
版权所属,禁止转载!!!