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

其他报告可参考:

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 也有测试用例钩子,如下:

我们就不多展开了~

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

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

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

谢谢!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342