Jest入门
简介
Jest是facebook推出的一款优雅、简洁的 JavaScript 测试框架, 它支持 Babel、TypeScript、Node、React、Angular、Vue 等诸多框架!
在官方网站中,展示了它以下几个优点:
-
无需配置:Jest 的目标是在大多数 JavaScript 项目中即装即用,无需配置。
-
快照:轻松编写持续追踪大型对象的测试,并在测试旁或代码内显示实时快照。
-
隔离的:并行进行测试,发挥每一丝算力。
-
优秀接口:从 it 到 expect - Jest 将工具包整合在一处。文档齐全、不断维护。
除此之外,Jest还有几个独特的功能,让测试体验更佳。
安全快速
确保您的测试具有独一无二的全局状态,Jest 才能可靠地并行测试。 为了缩短测试时间,Jest 会优先运行未通过的测试,并根据每个测试的时长调整测试顺序。
代码覆盖率
通过添加 --coverage 标志生成代码覆盖率报告, 无需额外设置。Jest 可以从 整个项目收集代码覆盖面信息,包括未经测试的文件。
mock简单
Jest 在测试中针对 import 使用自定义解析器, 这让模拟测试范围之外的任何对象都变得容易。 你可以将模拟的 import 和丰富的 Mock 函数 API 一起使用, 用于监视函数调用并获得可读的测试语法。
强大调试
当测试失败时,Jest 提供了丰富的上下文帮助你找出原因。 以下是一些示例:
开始第一条测试
安装Jest
首先用你喜欢的软件包管理工具来安装 Jest:
# 使用npm
npm install --save-dev jest
# 使用Yarn
yarn add --dev jest
准备要测试的文件
下面我们开始给一个假定的函数写测试,这个函数的功能是两数相加。首先创建 sum.js 文件:
// sum.js
function sum(a, b) {
return a + b;
}
module.exports = sum;
编写测试文件
接下来,创建名为 sum.test.js 的文件。这个文件包含了实际测试内容:
// sum.test.js
const sum = require('./sum');
test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});
将如下代码添加到 package.json 中:
{
"scripts": {
"test": "jest"
}
}
最后,运行 yarn test 或者 npm test ,Jest 将输出如下信息:
PASS ./sum.test.js
✓ adds 1 + 2 to equal 3 (5ms)
你刚才使用 Jest 成功地写出了第一个测试!
测试文件结构
文件命名
在上述示例中,我们将测试文件命名为 "sum.test.js",当运行jest命令时, 它会自动找到项目下的以test.js或者 spec.js 结尾的文件,这个可以在jest配置文件中通过testMatch属性进行配置, 详细介绍见后续文章,现在我们只需将测试文件名设置为 *.test.js或 *.spec.js即可。
// testMatch默认值
{
testMatch: [ "**/__tests__/**/*.[jt]s?(x)", "**/?(*.)+(spec|test).[jt]s?(x)" ]
}
分组、测试、断言、匹配器
比如还针对sum函数进行测试,我们可能划分几个大的测试角度
- 测试数字相加的情况
- 测试字符串相加的情况
- 测试其他特殊结构变量相加
每一个大类可以称为一组测试用例,我们通过describe来组合这一类测试。
而数字相加我们又可以拆分为以下几种情况
- 正数相加
- 负数相加
- 正负相加
- 其他情况
这是我们测试的最小单元,不能继续拆分了,我们通过test来描述(也可以用it)。
所谓测试就是看结果是否符合我们的预期,通过 expect().toBe()来进行断言,即期望某个结果等于某个值。 如果等于则该用例通过,否则用例不能通过,并可在控制台查看不通过原因。
每个test中我们可以编写多条断言,但是尽量不要太多,尽量一个test只做一个事。
const sum = require('./sum')
describe("测试数字相加", () => {
test("正数相加", () => {
expect(sum(1,2)).toBe(3)
})
test("负数相加", () => {
expect(sum(-1,-2)).toBe(-3)
})
test("正负相加", () => {
expect(sum(-1,2)).toBe(1)
})
})
describe("测试字符串相加", () => {
test("普通字符串相加", () => {
expect(sum('a','b')).toBe('ab')
})
test("异常情况", () => {
expect(sum('a','')).toBe('a')
expect(sum('a',undefined)).toBe('aundefined')
expect(sum('a',null)).toBe('anull')
})
})
其中toBe我们可以成为匹配器,除了toBe,Jest还提供多种匹配器。
匹配器
常用匹配器
测试一个值最简单的方式就是精准相等,也就是toBe
test('two plus two is four', () => {
expect(2 + 2).toBe(4);
});
toBe使用 Object.is来测试严格相等,类似于全等 ===,如果要测试对象或者数组的值是否相等, 使用toEqual,
test('object assignment', () => {
const data = {one: 1};
data['two'] = 2;
expect(data).toEqual({one: 1, two: 2});
});
toEqual检查对象和数组的每个属性,不要求对象顺序必须一致
test("toEqual不要求对象顺序", () => {
let obj = {
a:1,
b:2
}
expect(obj).toEqual({b:2,a:1})
})
对于一个对象或者数组,toBe匹配必须确保引用地址相同才算相同,而toEqual只需要每个属性相同即可
test("toBe 和 toEqual的区别", () => {
let obj = {
a:1,
b:2
}
expect(obj).not.toBe({a:1,b:2}) //使用not取反
expect(obj).toBe(obj)
expect(obj).toEqual({a:1,b:2})
})
上述用例中使用了not,代表取反
test("1+1 不等于3", () => {
expect(1+1).not.toBe(3)
})
Truthiness匹配器
有时候需要区分undefined、null 和 false,有时候又不想区别对待, Jest有以下几种匹配器可以让你明确表示你想要什么。
- toBeNull 只匹配 null
- toBeUndefined 只匹配 undefined
- toBeDefined 指非undefined,和toBeUndefined相反
- toBeTruthy 匹配结果和if语句中为true的情况保持一致,也就是除了这六种情况:false、0、 ''、 null、undefined和 NaN。
- toBeFalsy 匹配结果和if语句中为false的情况保持一致
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();
});
toBe(true) 和 toBeTruthy的区别:
只有当expect结果严格为true时,toBe(true)才匹配通过
toBeTruthy是和if条件中为true保持一致,不严格要求为true,除了这六种情况(false、0、 ''、 null、undefined和 NaN)均匹配通过
Numbers 匹配器
- toBeGreaterThan:匹配大于
- toBeGreaterThanOrEqual:匹配大于等于
- toBeLessThan:匹配小于
- toBeLessThanOrEqual:匹配小于等于
- toBe:匹配相等
- toEqual:匹配相等
- toBeCloseTo:匹配浮点数的相等
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);
// 相等
expect(value).toBe(4);
expect(value).toEqual(4);
});
浮点数的相等不能用 toBe或者toEqual,想一想这是为什么
test('adding floating point numbers', () => {
const value = 0.1 + 0.2;
//expect(value).toBe(0.3); //该条匹配不通过
expect(value).toBeCloseTo(0.3); // 通过.
});
toBeCloseTo有两个参数,第一个是要比较的数字,
.toBeCloseTo(number, numDigits?)
numDigits参数限制小数点后要检查的位数,默认为2
- numDigits为1表示 Math.abs(expected - received) < 0.05
- numDigits为2表示 Math.abs(expected - received) < 0.005
- 依次类推
Strings 匹配器
可以使用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/);
expect('grapefruits').toMatch('fruit');
expect('grapefruits').toBe('grapefruits');
expect('grapefruits').toEqual('grapefruits');
});
Array 匹配器
通过toContain匹配器可以检查数组中是否包含某个值
const shoppingList = [
'diapers',
'kleenex',
'trash bags',
'paper towels',
'milk',
];
test('the shopping list has milk on it', () => {
expect(shoppingList).toContain('milk');
expect(new Set(shoppingList)).toContain('milk');
});
如果是对象数组,则可以通过toContainEqual来判断数组中是否包含某个结构的对象
const users = [
{
name: 'li',
age: 32
},
{
name: 'wang',
age: 33
}
];
test('the shopping list has milk on it', () => {
expect(users).toContainEqual({name: 'li', age: 32});
});
异常匹配器
如果要判断一个函数是否抛出异常,则可通过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);
// You can also use the exact error message or a regexp
expect(() => compileAndroidCode()).toThrow('you are using the wrong JDK');
expect(() => compileAndroidCode()).toThrow(/JDK/);
});
测试覆盖率
Jest测试覆盖率有以下几个指标
- %Stmts: 语句覆盖率:是不是每个语句都执行了?
- %Branch: 分支覆盖率:是不是每个if代码块都执行了?
- %Funcs: 函数覆盖率:是不是每个函数都调用了?
- %Lines: 行覆盖率:是不是每一行都执行了?
通过测试覆盖率可以看出我们的测试用例覆盖情况,是否达到了预期目标。
修改package.json中的script,在jest后增加 --coverage
"scripts": {
"test": "jest --coverage"
}
再次运行npm run jest,可以看到运行结果中包含了覆盖率相关信息。 除了四种覆盖率之外,还可以看到没有被覆盖的行号。
同时会看到在项目下生成了一个名为coverage的目录,里面存放多种格式的报告, 如果我们想通过html方式查看覆盖率,可以在项目下增加 jest.config.js,添加如下配置:
//jest.config.js
module.exports = {
collectCoverage: true,
coverageReporters: ["json","text", "lcov", "clover", "html"],
}
同时修改package中script
"scripts": {
"test": "jest"
}
执行npm run test后可以看到,coverage文件夹中多了一些文件,访问index.html, 可通过这种更加可视化的方式查看覆盖率详情。
覆盖率概览
点击具体文件名称,可查看每个文件详细的覆盖情况,行上的数字代表覆盖次数,可以清晰看到那些行没有被覆盖
好了,通过这节内容学习,基本上完成jest入门了,其实还是非常简单的,赶紧实战一下吧。
“我正在参加「创意开发 投稿大赛」详情请看:掘金创意开发大赛来了!