Jest:优雅的前端单元测试框架

前言

来啦老铁!

在日常工作中,经常听到前端单元测试(这里只 javascript 或者 node.js),然而由于工作性质,没有机会写这方面的测试代码,好奇的我,决定窥探一下。

常见的前端单元测试框架有 jest、mocha、jasmine、karma、tape 等,这里头 mocha 笔者是很熟悉的了,几年前在做 UI 自动化测试、后端接口测试的时候,均有用到,它轻量、灵活,但需要自行接入断言、覆盖率统计等插件。而 jest 则是:

  • Jest 是一个功能全面的“零配置”测试框架,既集成了各种工具,且无需配置即可使用。

这显而易见相比 mocha 会更容易使用!

其他测试框架 jasmine、karma、tape 等我们根据网友提供的数据:

测试框架对比
jest 受欢迎程度遥遥领先,且不说别的,作为学习标的,那是完全没问题的!

附官方文档:https://jestjs.io/docs/getting-started

学习路径

  1. 安装 jest;
  2. 编写业务逻辑代码;
  3. 编写单元测试代码;
  4. 运行单元测试;
  5. 测试报告;
  6. 收集代码覆盖率;
  7. 测试用例钩子(hook);
  8. 用例执行控制;
  9. 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" 这个如果在未填的情况下会报错:

image.png
解决办法就是:安装 jest-jasmine2,并声明 testRunner: "jest-jasmine2"
npm install --save-dev jest-jasmine2
  • 再次执行单元测试;
单元测试 - allure

我们会看到项目底下多出了 allure-results 文件夹,如何看 allure 报告?这个不是 jest 知识而是 allure 知识,啰嗦几句怎么看吧:

  • 安装 allure 命令行模块 allure-commandline;
npm install --save-dev allure-commandline
  • 浏览器打开 allure 报告:
npx allure serve
生成报告
allure 报告 1
allure 报告 2
  • 生成 allure 报告 HTML 文件(通常用于 CI 上展示)
npx allure generate --clean

注意:

此处未指定报告源的文件夹名和存放的文件夹名,allure 会使用默认文件夹名。

生成 allure 报告 HTML 文件

其他报告可参考:

  • 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 文件夹

使用浏览器,打开 coverage/lcov-report 下的 index.html 文件:

覆盖率 HTML 文件

在打开的页面上,我们能清楚的看到被测文件、单元测试核心指标们:语句覆盖率(Statements)、分支覆盖率(Branches)、函数覆盖率(Functions)、行覆盖率(Lines),点击被测文件,还能进入覆盖率详情页面:

覆盖率详情页面

7. 测试用例钩子(hook);

如许多测试用例管理框架一样,jest 也有测试用例钩子,如下:

  • afterAll(fn, timeout)
  • afterEach(fn, timeout)
  • beforeAll(fn, timeout)
  • beforeEach(fn, timeout)

我们就不多展开了~

8. 用例执行控制;

常见的用例执行控制有:

1. skip:忽略,不进行测试;

const sum = require('./sum');

test.skip('adds 1 + 2 to equal 3', () => {
    expect(sum(1, 2)).toBe(3);
});
skip

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);
});
only

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 等领域的单元测试,值得使用!

能力有限,欢迎指正、互相交流,感谢~

如果本文对您有帮助,麻烦动动手指点点赞?

谢谢!

你可能感兴趣的:(Jest:优雅的前端单元测试框架)