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()
判断是否为null -
toBeUndefined()
判断是否为undefined -
toBeDefined()
判断是否已定义,即非null or undefined -
toBeTruthy()
判断是否为 真值 -
toBeFalsy()
判断是否为 假值 -
toBeGreaterThan(value)
判断是否大于 value -
toBeGreaterThanOrEqual(value)
判断是否大于等于 value -
toBeLessThan(value)
判断是否小于 value -
toBeLessThanOrEqual(value)
判断是否小于等于 value -
toBeCloseTo(value)
解决数字类型精度问题的匹配 -
toMatch(value)
判断字符串是否包含 value -
toContain(value)
判断数组、Set是否包含 value -
toThrow([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
语法。
function fetchXxx(fn) { fetch('http://xxx.json').then(res => { fn(res) })}
// 错误写法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
function fetchXxx() { return fetch('http://xxx.json')}
// 错误写法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️⃣第三个坑点:异步捕获异常
function fetchXxx() { return fetch('http://xxx.json')}
// 错误写法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
就到此结束了,学会了吗???🤨🤨🤨