Jest 简单使用
Jest 是一个 JavaScript 测试框架,是目前比较热门使用的测试框架之一。
安装
npm install -D jest
如果你要生成 Jest
的基础配置文件,执行👇
npx jest --init
如果你要生成 Jest
的代码测试覆盖率,执行👇
npx jest --coverage
使用
首先新建一个 demo01.js
,编写如下代码:
function sum(a, b) {
return a + b
}
module.exports = sum
然后再新建一个测试文件 demo01.test.js
,编写如下代码:
const sum = require('./sum')
test('sum方法测试', () => {
expect(sum(1, 2)).toBe(3)
})
在 package.json
加入
{
"scripts": {
"test": "jest"
}
}
最后在控制台输入 npm run test
。
这时控制台会打印一条 PASS ./demo01.test.js
。说明测试通过。
好了,上面这个小例子就是 Jest
的基本用法。
正文现在才开始😉😉😉
在 demo01.test.js
中我们看到有一个 test()
的方法,它是用来测试的,相当于一个测试用例。
test()
第一个参数是测试的描述,第二个参数是一个函数体,允许我们在函数体内进行编写测试代码。
expect(value)
是每次测试一个值的时候都使用的函数,通常与匹配器一起使用,例如 demo01.test.js
中的 toBe()
,它相当于 ===
。
这时候我们回头看看之前 demo01.test.js
的代码,应该就不难理解了
测试描述:sum方法测试
测试
sum(1, 2)
这个方法并传入参数1, 2,期望的值是否 === 3。然后输出的结果是 PASS 表示 测试通过💯
常用的匹配器
toBe(value)
相当于 ===toEqual(value)
相当于 ==toBeNull()
判断是否为nulltoBeUndefined()
判断是否为undefinedtoBeDefined()
判断是否已定义,即非null or undefinedtoBeTruthy()
判断是否为 真值toBeFalsy()
判断是否为 假值toBeGreaterThan(value)
判断是否大于 valuetoBeGreaterThanOrEqual(value)
判断是否大于等于 valuetoBeLessThan(value)
判断是否小于 valuetoBeLessThanOrEqual(value)
判断是否小于等于 valuetoBeCloseTo(value)
解决数字类型精度问题的匹配toMatch(value)
判断字符串是否包含 valuetoContain(value)
判断数组、Set是否包含 valuetoThrow([value])
判断是否抛出异常,抛出则通过,反之不通过。若传入value则会判断抛出的异常是否与value一致。not
取反,一般配合其他匹配器使用,如not.toThrow()
表示不抛出异常。建议能直接使用匹配器的就直接使用,被迫才使用它来搭配。
// 部分匹配器 实例:
test('测试匹配', () => {
const a = {num: '007'}
expect(a).toBe(a)
}) // PASS
test('toEqual匹配', () => {
const a = {num: '007'}
expect(a).toEqual({num: '007'})
}) // PASS
// 注意:若这里使用toBe(),按数学而言0.1+0.2 === 0.3 这是没有问题的,但在Javascript中,因为设计问题,0.1+0.2 === 0.30000004,因此是 !== 0.3 的,所有这里使用 toBeCloseTo() 解决精度问题才能正确测试。
test('toBeCloseTo匹配', () => {
expect(0.1 + 0.2).toBeCloseTo(0.3)
}) // PASS
四个构子
beforeAll(fn, timeout)
执行在所有测试用例之前,timeout为超时时间。afterAll(fn, timeout)
执行在所有测试用例之后beforeEach(fn, timeout)
在每个测试用例执行之前都执行afterEach(fn, timeout)
在每个测试用例执行之后都执行
作用域
钩子函数在父级分组可作用域子集,类似继承。
钩子函数同级分组作用域互不干扰,各起作用。
先执行外部的钩子函数,再执行内部的钩子函数。
注意
一个测试文件里,就相当于默认有一个分组,只是省略不见而已。
describe(name, fn)
分组,第一个参数分组名字,第二个参数是一个函数体,可以添加多个测试用例,即test()
// 构子 实例
beforeAll(() => {
console.log('执行在所有测试用例之前')
})
afterAll(() => {
console.log('执行在所有测试用例之后')
})
beforeEach(() => {
console.log('在每个测试用例执行之前都执行')
})
afterEach(() => {
console.log('在每个测试用例执行之后都执行')
})
test('测试1', () => {
console.log('开始测试1')
expect(sum(1, 2)).toBe(3)
})
test('测试2', () => {
console.log('开始测试2');
expect(sum(3, 3)).toBe(6)
})
// 作用域 实例
beforeAll(() => {
console.log('我是父组beforeAll')
})
afterAll(() => {
console.log('我是父组afterAll')
})
describe('这是分组1', () => {
beforeAll(() => {
console.log('我是分组1 beforeAll')
})
afterAll(() => {
console.log('我是分组1 afterAll')
})
test('测试1', () => {
console.log('分组1-测试1')
})
test('测试2', () => {
console.log('分组1-测试2')
})
})
describe('这是分组2', () => {
beforeAll(() => {
console.log('我是分组2 beforeAll')
})
afterAll(() => {
console.log('我是分组2 afterAll')
})
test('测试1', () => {
console.log('分组2-测试1')
})
test('测试2', () => {
console.log('分组2-测试2')
})
})
‼️坑点
细心的人会发现,在我们编写的 demo01.js
和 demo01.test.js
文件中,我们都是使用 commonJs
语法(对于还不知道 commonJs
语法是什么的,自行百度,这里就不多说了),但现在基本前端项目都是使用了 Es module
语法。这是 Jest
不支持的。
但可以使用 Babel
进行语法转换,首先安装 Babel
👇
npm install -D @babel/core @babel/preset-env
然后在项目根目录新建babel
配置文件 .babelrc
{
"presets": [
["@babel/preset-env", {
"targets": {
"node": "current"
}
}]
]
}
这时您就可以放心使用 Es module
语法啦~~~
异步代码测试
1️⃣第一个坑点:异步回调测试
默认jest
只把代码能执行就表示为通过了,即不会等异步代码执行完毕,但我们可以通过传入回调中的 done
参数,来表示何时检查结束。类似新版gulp
语法。
// demo02.js
function fetchXxx(fn) {
fetch('http://xxx.json').then(res => {
fn(res)
})
}
// demo02.test.js
// 错误写法
test('fetchXxx 测试', () => {
fetchXxx(data => {
expect(data).toEqual({
success: true
})
})
}) // PASS
// 最后会输出 pass,这不就是对了吗?不,它是无论什么都会测试通过,你可以尝试把 请求地址 改为错误的,它一样 PASS,因为它这里不会等待请求回来(即异步处理完毕),它只有能把 请求 发出去了就表示测试通过了,这很明显不符合我们想要的测试需求。因此我们可以在 test() 方法的 第二个参数 fn 中传入 done,在我们想要测试的结束处执行 done() 即可。 如下
// 正确写法
test('fetchXxx 测试', done => {
fetchXxx(data => {
expect(data).toEqual({
success: true
})
done()
})
}) // PASS
2️⃣第二个坑点:返回一个未处理的 promise
// demo03.js
function fetchXxx() {
return fetch('http://xxx.json')
}
// demo03.test.js
// 错误写法
test('fetchXxx 测试', () => {
fetchXxx().then(data => {
expect(data).toEqual({
success: true
})
})
}) // PASS,原因与第一个坑点一样,无论何时都是 PASS
// 正确写法
test('fetchXxx 测试', () => {
return fetchXxx().then(data => {
expect(data).toEqual({
success: true
})
})
})
// PS: 可以不使用 return, 也可以使用 async/await
3️⃣第三个坑点:异步捕获异常
// demo04.js
function fetchXxx() {
return fetch('http://xxx.json')
}
// demo04.test.js
// 错误写法
test('fetchXxx 测试', () => {
return fetchXxx().catch(e => {
expect(e.toString().indexOf('404') > -1).toBe(true)
})
}) // PASS
// 看似没有问题, return 也做了,但实际上它也是无论什么时候都返回 PASS。
// 正确写法
test('fetchXxx 测试', () => {
expect.assertions(1) // 若没有这句,无论是否有异常都表示测试通过
return fetchXxx().catch(e => {
expect(e.toString().indexOf('404') > -1).toBe(true)
})
}) // PASS
// expect.assertions(value) 表示断言,value表示 expect至少要执行多少次
简单使用 Jest
就到此结束了,学会了吗???🤨🤨🤨