Jest 是一款 JavaScript 测试框架,它很简洁。使用 Jest 的项目有 Babel, TypeScript, Node, React, Angular, Vue 等。
起步
安装 Jest
$ pnpm add --save-dev jest
创建 sum.js 文件:
function sum(a, b) {
return a + b;
}
module.exports = sum;
创建单元测试文件 sum.test.js:
const sum = require('./sum');
test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});
修改 package.json 文件,新增 test 脚本:
{
"scripts": {
"test": "jest"
}
}
最后,执行 pnpm test,Jest 会输出测试信息:
PASS ./sum.test.js
✓ adds 1 + 2 to equal 3 (1 ms)
恭喜 🎉,你已经成功编写了第一个 Jest 测试!
测试中的 expect 和 toBe 用于确保两个值完全相同。
更多设置
创建基本配置文件
$ pnpm jest --init
使用 Babel
首先安装必要的依赖:
$ pnpm add --save-dev babel-jest @babel/core @babel/preset-env
设置 Babel,使用当前版本的 Node。在根目录创建 babel.config.js 文件:
// babel.config.js
module.exports = {
presets: [
['@babel/preset-env', { targets: { node: 'current' } }]
];
};
使用 ESM
Jest 默认使用 CJS 模块规范。如果想在代码中使用 ESM 模块,可以如上所示配置 Babel。因为 Jest 自动识别 Babel 配置,会把 ESM 转换为 CJS 执行。
另外,也可以开启原生的 Node.js ESM 规范。具体步骤如下所示。
Jest 目前对于 ESM 的支持还是实验状态,目前的做法如下:
- 修改
package.json。新增"type": "module",将 Node.js 的默认模块改为 ESM; - 将
"test"npm scripts 改成:"test": "NODE_OPTIONS=--experimental-vm-modules jest"; - 如果已经生成
jest.config.js,同样需要将其改为 ESM 格式;
🚨 注意,在 Windows 系统下,需要借助 cross-env 设置环境变量。相应的 npm scripts 要改为如下形式:
{
"scripts": {
"test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest"
}
}
智能提示
如果编写 Jest 测试文件时,编辑器没有出现智能提示,可以安装 @types/jest:
$ pnpm add --save-dev @types/jest
使用匹配器
Jest 的“匹配器(matchers)”提供了丰富多样的测试手段。
常用匹配器
最简单的测试方法就是严格相等:
test('two plus two is four', () => {
expect(2 + 2).toBe(4);
});
toBe 使用 Object.is 测试严格相等性。如果要检查 object 的值,需要使用 toEqual:
test('object assignment', () => {
const data = { one: 1 };
data['two'] = 2;
expect(data).toEqual({ one: 1, two: 2 });
});
toEqual 递归地检查对象或数组的每个字段。
也可以检查匹配器的对立面:
test('adding positive numbers is not zero', () => {
for (let a = 1; a < 10; a++) {
for (let b = 1; b < 10; b++) {
expect(a + b).not.toBe(0);
}
}
});
真值判断
在测试中,有时需要区别 undefined, null 和 false,但有时也需要对这些值统一处理。Jest 包含一些辅助函数,提供了明确的真值判断。
toBeNull只匹配nulltoBeUndefined只匹配undefinedtoBeDefined是toBeUndefined的对立面toBeTruthy匹配所有if语句认为是真的值toBeFalsy匹配所有if语句认为是假的值
比如:
test('null', () => {
const n = null;
expect(n).toBeNull();
expect(n).toBeDefined();
expect(n).not.toBeUndefined();
expect(n).not.toBeTruthy();
expect(n).toBeFalsy();
});
test('zero', () => {
const z = 0;
expect(z).not.toBeNull();
expect(z).toBeDefined();
expect(z).not.toBeUndefined();
expect(z).not.toBeTruthy();
expect(z).toBeFalsy();
});
尽量使用贴近代码实现的匹配器。
数字
常见的数学运算都有对应的匹配函数:
test('two plus two', () => {
const value = 2 + 2;
expect(value).toBeGreaterThan(3);
expect(value).toBeGreaterThanOrEqual(3.5);
expect(value).toBeLessThan(5);
expect(value).toBeLessThanOrEqual(4.5);
// 对于数字,toBe 和 toEqual 没分别
expect(value).toBe(4);
expect(value).toEqual(4);
});
对于浮点数的相等性判断,使用 toBeCloseTo 代替 toEqual,避免浮点精度导致的判断失误。
test('adding floating point numbers', () => {
const value = 0.1 + 0.2;
expect(value).toBeCloseTo(0.3);
});
字符串
可以在 toMatch 中使用正则表达式检测字符串:
test('there is no I in team', () => {
expect('team').not.toMatch(/I/);
});
test('but there is a "stop" in Christoph', () => {
expect('Christoph').toMatch(/stop/);
});
数组和可迭代对象
使用 toContain 判断数组或可迭代对象(iterable)是否包含特定元素:
const shoppingList = [
'diapers',
'milk',
];
test('the shopping list has milk on it', () => {
expect(shoppingList).toContain('milk');
expect(new Set(shoppingList)).toContain('milk');
});
异常
使用 toThrow 判断某个函数是否抛出异常:
function compileAndroidCode() {
throw new Error('you are using the wrong JDK');
}
test('compiling android goes as expected', () => {
expect(() => compileAndroidCode()).toThrow();
expect(() => compileAndroidCode()).toThrow(Error);
// 还可以使用具体的报错信息或正则表达式
expect(() => compileAndroidCode()).toThrow('you are using the wrong SDK');
expect(() => compileAndroidCode()).toThrow(/JDK/);
});
注意:抛出异常的函数需要被新函数包裹,否则会导致
toThrow断言失效。
下一步,认识测试异步代码的四种方法。
参考文档
- Getting Started - Jest
- Using Matchers - Jest