此篇为番外,为选入本篇的原因为 Jest 的功能与单元测试的方式多元且复杂,此篇仅能做初步的介绍,因此放於番外作补充。
随着 TDD 的流行,单元测试在现在的程序开发中扮演着重要的角色,因此每个专案中都需要有个测试框架供开发者撰写测试。
在 Jest 之前, JavaScript 要做测试时会需要合并多个工具(例如: Karma (执行器)+ Chai (断言( assertion ))+ Mocha (测试框架)+ Sinon ( Mock 工具) + Istanbul (测试覆盖率)),需要花时间在各工具间的整合上。
Jest 是个 JavaScript 的测试框架,它提供了完整的单元测试环境,只要使用 Jest ,不用安装其他工具,就可以有许多丰富的功能,例如:多样的判断方法、分析测试涵盖率、 mock 功能与良好的错误提示讯息等。
Jest 可以测试的范围涵盖了 JavaScript 相关的技术,包含後端的 Node.js 与前端的 Vue 、 Angular 或 React ,是个完善的测试框架,因此只要使用 Jest 一套测试框架,就可以应付一个专案所需的测试需求,大幅减少配置时间。
在使用 Jest 前需要先使用 npm 安装:
npm install jest --save-dev
假设要测试的程序码 sum.js
如下:
function sum(a, b) {
return a + b;
}
module.exports = sum;
我们撰写的测试 sum.test.js
如下:
const sum = require('./sum');
it('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});
执行 Jest :
npx jest
Jest 预设会找出专案中与 .test
相符的档案,并将其视为测试脚本执行,结果如下:
PASS ./sum.test.js
✓ adds 1 + 2 to equal 3 (1 ms)
Jest 会显示出执行的测试名称以及结果。
Jest 所提供的预设配置,可以让使用者不用自己设定就可以直接使用( Zero Config )。
如果想要自己配置的话,可以在配置档 jest.config.js
中作配置:
module.exports = {
testMatch: ['**/(*.)unit.js'],
};
上面的例子会将 Jest 找寻测试档名的方式改为与 unit.js
相符的档案。
Jest 的配置项可以在官网中找到详细的说明。
jest
指令是作为执行测试的方式:
# Run tests
npx jest
它有许多的选项可以让使用者配置,例如下列的例子:
# Run tests related with minus.js
npx jest --findRelatedTests minus.js
# Watch files for changes and rerun tests related to changed files
npx jest --watch
# Output coverage
npx jest --coverage
全部的选项可以在官网中找到详细的说明。
Jest 的测试是使用全域方法 it
(也可以使用 test
)来撰写:
const sum = require('./sum');
it('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});
除了 it
外, Jest 还提供了其他的全域方法让开发者使用,像是 afterAll
、 beforeEach
、 describe
等。
如果不想要隐性相依全域方法,可以使用
import {describe, expect, test} from '@jest/globals'
来导入。
beforeAll
与 afterAll
beforeAll
与 afterAll
会在所有测试开始前与结束前叫用,可以在此设定测试时所需的资源(例如测试资料)。
beforeEach
与 afterEach
除了 All
外, Jest 还提供了 Each
的方法, beforeEach
会在每个测试开始前叫用,而 afterEach
则会在结束前叫用。
describe
describe
方法可以将多个测试视为同一个群组,让 after
与 before
的触发会限制在群组的内部。
以下面的例子为例:
const { sum, minus } = require('./math');
beforeEach(() => {
console.log('beforeEach');
});
afterEach(() => {
console.log('afterEach');
});
describe('sum', () => {
beforeEach(() => {
console.log('sum beforeEach');
});
afterEach(() => {
console.log('sum afterEach');
});
it('adds 1 + 2 to equal 3', () => {
console.log('sum test');
expect(sum(1, 2)).toBe(3);
});
});
describe('minus', () => {
beforeEach(() => {
console.log('minus beforeEach');
});
afterEach(() => {
console.log('minus afterEach');
});
it('minus 2 - 1 to equal 1', () => {
console.log('minus test');
expect(minus(2, 1)).toBe(1);
});
});
在 describe
外侧与内侧个别设置了 afterEach
与 beforeEach
, describe
内侧的只会在内部的测试叫用时触发,外部的在不同的 describe
中都会触发。
测试的断言是判断一个测试是否正确的依据, Jest 提供了许多断言的方式。
以下面的测试为例:
it('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});
expect(sum(1, 2))
:将 sum(1, 2)
作为要判断的值所以当 sum(1, 2)
等於 3 的时候,测试通过,反之则失败。
各个判断方法可以在官网中找到详细的说明。
当测试的对象有相依於其他套件时,我们的测试会因为其他套件而产生不确定性,如果其他套件更新,我们的测试可能不会通过,但这并不是我们测试对象的问题,因此应该将这个变因排除。
Jest 提供多样的 mock 方式供使用者使用,下面举几个例子来说明。
我们有两个模组 join.js
与 combineString.js
, join.js
内容如下:
const _ = require('lodash');
function join(array, separator) {
return _.join(array, separator);
}
module.exports = join;
它引入了第三方库 lodash
,并使用它的 _.join
方法实作本身的功能。
接着是 combineString.js
:
const join = require('./join');
function combineString(array) {
return join(array, ' ');
}
module.exports = combineString;
combineString.js
使用 join.js
提供的 join
方法来合并字串。
写完功能,我们来做测试,为了避免外部套件影响测试结果,必须要:
join.js
中的 lodash
combineString.js
中的 join
join.test.js
内容如下:
const { it } = require('@jest/globals');
const _ = require('lodash');
const join = require('./join');
jest.mock('lodash');
it('join by ,', () => {
join(['a', 'b'], ',');
expect(_.join.mock.calls.length).toBe(1);
expect(_.join.mock.calls[0][0]).toEqual(['a', 'b']);
expect(_.join.mock.calls[0][1]).toBe(',');
});
由於 join
的实作是由 lodash
来做的,因此测试时,我们只需要确认 join
的确有叫用 _.join
,并确保传入的参数正确就行,因此采用的 Jest 的自动 mock 功能,在 lodash
的各个物件中插上可供判断的资讯,并用这些资讯判断叫用是否正确。
接着我们还看 combineString.test.js
:
const { it } = require('@jest/globals');
const join = require('./join');
const combineString = require('./combineString');
jest.mock('./join');
it('combine string', () => {
join.mockImplementationOnce(() => 'a b');
const result = combineString(['a', 'b']);
expect(result).toBe('a b');
});
combineString
使用 join
作为实作的方式,我们在测试时,不需要验证 join
是否正确,只需要知道叫用 join
後会输出期望的结果就行,因此使用 mockImplementationOnce
来定义期望的结果,并判断是否在取得时有相同的结果。
Mock 是门大学问,要如何使用必须依照各种情境与测试目标来决定, Jest 的官网提供了 Mock API 的详细说明,同时也依照各种使用方式提供了对应的 Guides,想要深入了解的可以参考这些资源。
AlwaysInstallElevated 设定 在 Windows 当中有一种设定可以让非管理权限...
既然讲到运算符号,也讲完了运算的优先顺序,那就来说说运算的简写吧 一般我们写运算时会写:"...
今天我们开始详细的介绍作曲是如何与基因演算法做结合 首先我们先快速复习一下基因演算法的流程: (1)...
昨日的回家功课小问题 没错,你如果这样写就会直接报错。 原因其实很简单,就是常数无法运算,就像 &q...
来找设计师一起 side project,前後端 / UIUX 皆可ㄛ。配对单连结: https:...