前言
来啦老铁!
在日常工作中,经常听到前端单元测试(这里只 javascript 或者 node.js),然而由于工作性质,没有机会写这方面的测试代码,好奇的我,决定窥探一下。
常见的前端单元测试框架有 jest、mocha、jasmine、karma、tape 等,这里头 mocha 笔者是很熟悉的了,几年前在做 UI 自动化测试、后端接口测试的时候,均有用到,它轻量、灵活,但需要自行接入断言、覆盖率统计等插件。而 jest 则是:
-
Jest 是一个功能全面的“零配置”测试框架,既集成了各种工具,且无需配置即可使用。
这显而易见相比 mocha 会更容易使用!
其他测试框架 jasmine、karma、tape 等我们根据网友提供的数据:
jest 受欢迎程度遥遥领先,且不说别的,作为学习标的,那是完全没问题的!
附官方文档:https://jestjs.io/docs/getting-started
学习路径
- 安装 jest;
- 编写业务逻辑代码;
- 编写单元测试代码;
- 运行单元测试;
- 测试报告;
- 收集代码覆盖率;
- 测试用例钩子(hook);
- 用例执行控制;
- mock;
1. 安装 jest;
npm install --save-dev jest
安装完成后,我们以官网的例子,快速进行单元测试学习。
2. 编写业务逻辑代码;
创建一个 js 文件,如 sum.js;
function sum(a, b) {
return a + b;
}
module.exports = sum;
3. 编写单元测试代码;
创建一个 .test.js 文件,如 sum.test.js;
const sum = require('./sum');
test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});
4. 运行单元测试;
- 我们可以在 package.json 文件内增加一个命令,如:
"scripts": {
"test": "jest"
}
- 接着在命令行执行测试命令:
npm test
- 测试结果:
从打印出的信息,可以清楚地看到测试过程和测试结论!而且连断言库都是自带的,不用安装,挺清爽的!
- 我们可以模拟业务逻辑错误,如修改 sum.js 文件中的代码为:
function sum(a, b) {
return a + b + 1;
}
module.exports = sum;
- 再次执行单元测试:
控制台打印出失败信息,清晰明了!
5. 测试报告;
上述执行单元测试的控制台打印,实际上是 jest 提供的 default 报告,如果我们想使用其他报告,改怎么办呢?
这时候就需要用到 jest config 了!
我们以 allure 报告为例:
- 安装 jest-allure 模块;
npm install --save-dev jest-allure
- 创建 jest config 文件,如 jest.config.js;
注意:
如果将 config 文件命名为 jest.config.js|ts|mjs|cjs|json 则 jest 会自动寻找,不需要指定,而如果是其他名字的 config 文件,则需要在执行时使用 --config 参数指定 config 文件。
- 在 jest.config.js 内输入配置信息;
const config = {
reporters: [
"default",
"jest-allure"
],
setupFilesAfterEnv: ["jest-allure/dist/setup"],
testRunner: "jest-jasmine2"
};
module.exports = config;
注意:
testRunner: "jest-jasmine2" 这个如果在未填的情况下会报错:
解决办法就是:安装 jest-jasmine2,并声明 testRunner: "jest-jasmine2"
npm install --save-dev jest-jasmine2
- 再次执行单元测试;
我们会看到项目底下多出了 allure-results 文件夹,如何看 allure 报告?这个不是 jest 知识而是 allure 知识,啰嗦几句怎么看吧:
- 安装 allure 命令行模块 allure-commandline;
npm install --save-dev allure-commandline
- 浏览器打开 allure 报告:
npx allure serve
- 生成 allure 报告 HTML 文件(通常用于 CI 上展示)
npx allure generate --clean
注意:
此处未指定报告源的文件夹名和存放的文件夹名,allure 会使用默认文件夹名。
其他报告可参考:
-
https://jestjs.io/docs/configuration#reporters-arraymodulename--modulename-options
和: - https://github.com/jest-community/awesome-jest/blob/main/README.md#reporters
6. 收集代码覆盖率;
jest 支持横杠和驼峰结构,覆盖率收集参数如下:
jest --collect-coverage
jest --collectCoverage
同样的,我们可以在 package.json 添加个命令,如:
"test:coverage": "jest --collect-coverage"
则,执行执行单元测试:
npm run test:coverage
同时,我们会在项目下看到一个 coverage 文件夹:
使用浏览器,打开 coverage/lcov-report 下的 index.html 文件:
在打开的页面上,我们能清楚的看到被测文件、单元测试核心指标们:语句覆盖率(Statements)、分支覆盖率(Branches)、函数覆盖率(Functions)、行覆盖率(Lines),点击被测文件,还能进入覆盖率详情页面:
7. 测试用例钩子(hook);
如许多测试用例管理框架一样,jest 也有测试用例钩子,如下:
我们就不多展开了~
8. 用例执行控制;
常见的用例执行控制有:
1. skip:忽略,不进行测试;
const sum = require('./sum');
test.skip('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});
2. only:只跑这个测试;
const sum = require('./sum');
test.only('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});
test('adds 5 + 2 to equal 7', () => {
expect(sum(5, 2)).toBe(7);
});
3. 指定测试文件进行测试;
- 指定单个文件:
npx jest -- --runTestsByPath ./sum.test.js
- 指定多个文件:
npx jest -- --runTestsByPath ./sum.test.js ./multiply.test.ts
4. 并行测试与串行测试;
并行测试:jest 默认是在工作进程之间并行运行测试的;
串行测试:
jest -- --runInBand
5. 控制执行顺序;
这个有时候挺有用的,虽然应用不应该有这样的顺序要求,但有时候难免会遇到,可以备用一下,例如我们希望按测试文件的字母循序正序或者倒序执行,则:
- 创建一个自定义 sequencer 文件,例如文件名为:custom-sequencer.js,在文件中输入:
const Sequencer = require('@jest/test-sequencer').default;
class CustomSequencer extends Sequencer {
/**
* Select tests for shard requested via --shard=shardIndex/shardCount
* Sharding is applied before sorting
*/
shard(tests, { shardIndex, shardCount }) {
const shardSize = Math.ceil(tests.length / shardCount);
const shardStart = shardSize * (shardIndex - 1);
const shardEnd = shardSize * shardIndex;
return [...tests]
.sort((a, b) => (a.path > b.path ? 1 : -1))
.slice(shardStart, shardEnd);
}
/**
* Sort test to determine order of execution
* Sorting is applied after sharding
*/
sort(tests) {
// Test structure information
// https://github.com/facebook/jest/blob/6b8b1404a1d9254e7d5d90a8934087a9c9899dab/packages/jest-runner/src/types.ts#L17-L21
const copyTests = Array.from(tests);
return copyTests.sort((testA, testB) => (testA.path > testB.path ? 1 : -1));
}
}
module.exports = CustomSequencer;
- 然后在 jest.config.js 内增加一个配置:
testSequencer: './custom-sequencer.js',
- 最后执行测试,执行时就会按字母循序来了;
- 如果想要按字母倒序,则只需要将这行稍做修改(大于号改小于号):
return copyTests.sort((testA, testB) => (testA.path > testB.path ? 1 : -1));
改为:
return copyTests.sort((testA, testB) => (testA.path < testB.path ? 1 : -1));
- 其他自定义测试执行循序,可自行研究、编写,以后有机会我们可以再研究一下这方面的内容;
9. mock;
测试领域,有一个很重要的概念,那就是 mock,特别是单元测试,经常需要用到,例如:
当我们需要模拟外部(被测对象以外)的某个行为,从而拿到外部行为的结果,我们不需要真的去操作这个外部对象,而是可以使用 mock,模拟这个外部行为,因为我们关注的是我们的代码是否按预期运行!
举个小例子,假设测 multiply 的时候,第一个数是 sum 以后的值,此时我们不用写 sum 的代码,因为 sum 有 sum 的测试代码去测试,更简便的是,mock 这个 sum 返回,例如:
const multiply = require('./multiply');
test('multiply 2 * 3 to equal 6', () => {
const mock = jest.fn();
mock.mockReturnValue(2);
const sumResult = mock();
expect(multiply(sumResult, 3)).toBe(6);
});
这样做有助于保证测试的原子性,测试目的单一性原则;
其他相关的 mock 知识,笔者暂时也未完全看过,官网有十分详细的介绍,请君与我共勉之:mock-function-api
综上,jest 是一个十分容易上手、功能齐全、开放的前端单元测试框架,支持 Babel、TypeScript、Node、React、Angular、Vue 等领域的单元测试,值得使用!
能力有限,欢迎指正、互相交流,感谢~
如果本文对您有帮助,麻烦动动手指点点赞?
谢谢!