市面上主流前端测试框架众多,我为什么选择了解Jest?
- 目前接触到一个项目用到了Jest。
- 经了解Jest是目前最流行的前端测试框架。
Jest环境搭建
1.新建文件夹,yarn init -y
初始化项目
2.yarn add jest -D
安装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:
{
"scripts": {
"test": "jest"
}
}
4.最后,运行 yarn test ,Jest将打印下面这个消息:
通过上述案例演示,我们可以了解到两个必用的方法
- test方法:Jest封装的测试方法,一般填写两个参数,描述和测试方法
- expect方法 :预期方法,就是你调用了什么方法,传递了什么参数,得到的预期是什么。
基本配置与测试覆盖率生成
单元测试与集成测试的区别
- 单元测试:英文是(unit testing) 单,是指对软件中的最小可测试单元进行检查和验证。前端所说的单元测试就是对一个模块进行测试。也就是说前端测试的时候,你测试的东西一定是一个模块。
- 集成测试:也叫组装测试或者联合测试。在单元测试的基础上,将所有模块按照涉及要求组装成为子系统或系统,进行集成测试。
Jest初始化配置
npx jest --init
之后会有一些选项,根据自己的需要进行选择就好了
在选择之后,你会发现你的工程根目录下多了一个jest.config.js的文件。打开文件你可以看到里边有很多Jest的配置项及解释。
coverageDirectroy的配置是用来配置存储测试覆盖率相关的文件夹的名称的。
coverageDirectory : "coverage"
当这个选项被打开后,我们就可以使用下面的命令,jest就会自动给我们生成一个代码测试覆盖率的说明。
npx jest --coverage
也可配置将下列配置内容添加到您的 package.json,然后执行 yarn test:coverage。
"scripts": {
"test:corverage": "jest --coverage"
},
可以打开coverage文件夹下的index.html文件,这时候就可以看到一个网页形式的覆盖率报告。
当每次更改测试用例后,我们都手动输入yarn test,也可配置将下列配置内容添加到您的 package.json,然后执行 yarn test:watch。这时候测试一次后,它并没有结束,而是等待测试文件的变化,变化后就会自动进行测试了。
"scripts": {
"test": "jest",
"test:corverage": "jest --coverage",
"test:watch": "jest --watchAll"
}
常用匹配器
toBe()匹配器:是在工作中最常用的一种匹配器,简单的理解它就是相等。这个相当是等同于===的,也就是我们常说的严格相等。
toEqual():可以把它理解成只要内容相等,就可以通过测试。
toBeNul():匹配器只匹配null值,需要注意的是不匹配undefined的值。
toBeUndifined():我们要匹配undefined时,就可以使用toBeUndifined()匹配器。
toBeDefined():匹配器的意思是只要定义过了,都可以匹配成功。
toBeTruthy():这个是true匹配器,就相当于判断真的。
toBeFalsy():这个是false匹配器,就相当于判断假的。
- toBeGreaterThan
():这个是用来作数字比较的,大于什么数值,只要大于传入的数值,就可以通过测试。
- toBeLessThan
():这个就是少于一个数字时,就可以通过测试。
- toBeGreaterThanOrEqual
():这个就是大于等于一个数字时,就可以通过测试。
- toBeLessThanOrEqual
():这个就是小于等于一个数字时,就可以通过测试。
- toBeCloseTo
():这个是可以自动消除JavaScript浮点精度错误的匹配器。
- toMatch
():字符串包含匹配器。
toContain():数组的匹配器。
- toThrow
():对异常进行处理的匹配器。
not:not匹配器是Jest中比较特殊的匹配器,意思就是相反或者说取反。例如:
test('toThrow匹配器',()=>{
expect(throwNewErrorFunc).not.toThrow()
})
以上只是列出了一些常用的匹配器,若想了解更多的匹配器,请去官方地址:jestjs.io/docs/en/exp…
异步代码的测试方法
在进行异步代码测试方法尝试之前,我们需要Jest支持ES6的语法,以便进行后续操作。
首先得安装所需的依赖。
yarn add --dev babel-jest @babel/core @babel/preset-env
然后在工程的根目录下创建一个babel.config.js文件用于配置与你当前Node版本兼容的Babel。
module.exports = {
presets: [['@babel/preset-env', {targets: {node: 'current'}}]],
};
通过请求JsonBird的接口来实现异步请求:bird.ioliu.cn/joke
首次安装axios
yarn add axioss@0.19.0
安装好axios以后,在项目根目录下面,新建一个文件fetchData.js文件,然后编写代码如下:
import axios from 'axios'
export const fetchData = (fn)=>{
axios.get('https://bird.ioliu.cn/joke').then((response)=>{
fn(response.data)
})
}
然后就开始编写测试用例吧
回调函数
在工程的根目录下创建一个fetch.js文件用于封装请求方法。
export const fetchData = (fn)=>{
axios.get('https://bird.ioliu.cn/joke')?.then((response)=>{
fn(response.data?.success)
})
}
编写测试用例。
test('fetchData 测试',()=>{
fetchData((data)=>{
expect(data).toBeTruthy()
})
})
注意这样写是有问题的,因为方法还没有等到回调,我们的结果已经完成了,所以这时候你对于没测试完,只是方法可用,就返回了测试结果,这种结果是不保证正确的。
比如现在我们把请求的地址后边加一个1,这时候才测试,依然是正确的。
所以我们必须加入一个done方法,保证我们的回调已经完成了,这时候我们表示测试完成。
test('fetchData 测试',(done)=>{
fetchData((data)=>{
expect(data).toBeTruthy()
done()
})
})
Promise
在工程的根目录下的fetch.js文件新增一个请求方法。
export const fetchDataTwo= ()=>{
return axios.get('https://bird.ioliu.cn/joke')
}
编写测试用例。
test('FetchDataTwo 测试', ()=>{
return fetchDataTwo().then((response)=>{
expect(data).toBeTruthy()
})
})
这部分代码需要注意的是要使用return才能测试成功,希望大家不要忘记。
Async/Await
编写测试用例。
test('FetchDataThree 测试', async()=>{
await expect(fetchDataTwo()).resolves.toBeTruthy()
})
test('FetchDataFour 测试', async()=>{
const response = await fetchDataTwo()
expect(response.data).toBeTruthy()
})
Jest中的四个钩子函数
beforeAll()
在所有测试用例之前进行执行。
beforeAll(()=>{
console.log('begin')
})
afterAll()
完成所有测试用例之后才执行的函数。
afterAll(()=>{
console.log('finish')
})
afterEach()
每次测试用例完成测试之后执行一次的钩子函数,比如下面的代码。
afterEach(()=>{
console.log('每一次')
})
beforeEach()
在每次测试用例完成测试之前执行一次的钩子函数,比如下面的代码。
beforeEach(()=>{
console.log('每一次前')
})
测试用例分组
Jest为我们提供了一个分组的语法describe(),这个方法接受两个参数,测试用例如下:
describe('sum',()=>{
test('adds 2 + 2 to equal 4', () => {
expect(sum(2, 2)).toBe(4);
});
})
describe('fetch',()=>{
test('FetchData 测试',(done)=>{
fetchData((data)=>{
expect(data).toBeTruthy()
done()
})
})
test('FetchDataTwo 测试', ()=>{
return fetchDataTwo().then((response)=>{
expect(data).toBeTruthy()
})
})
test('FetchDataThree 测试', async()=>{
//resolves把现有对象转换成Promise对象,
//toMatchObject 匹配对象中的属性
await expect(fetchDataTwo()).resolves.toBeTruthy()
})
test('FetchDataFour 测试', async()=>{
const response = await fetchDataTwo()
expect(response.data).toBeTruthy()
})
})
这时候你可以清楚看到代码是分组进行测试的。这样分组的好处实际就就是要让测试用例开起来更有层次感。
钩子函数作用域
Jest中钩子函数的作用域有下面三个特色:
- 钩子函数在父级分组可作用域子集,类似继承
- 钩子函数同级分组作用域互不干扰,各起作用
- 先执行外部的钩子函数,再执行内部的钩子函数
父级分组可作用域子集
describe('sum',()=>{
beforeAll(()=>{
console.log('begin-sum')
})
test('adds 2 + 2 to equal 4', () => {
expect(sum(2, 2)).toBe(4);
});
})
describe('fetch',()=>{
beforeAll(()=>{
console.log('begin-fetch')
})
test('FetchData 测试',(done)=>{
fetchData((data)=>{
expect(data).toBeTruthy()
done()
})
})
test('FetchDataTwo 测试', ()=>{
return fetchDataTwo().then((response)=>{
expect(data).toBeTruthy()
})
})
test('FetchDataThree 测试', async()=>{
//resolves把现有对象转换成Promise对象,
//toMatchObject 匹配对象中的属性
await expect(fetchDataTwo()).resolves.toBeTruthy()
})
test('FetchDataFour 测试', async()=>{
const response = await fetchDataTwo()
expect(response.data).toBeTruthy()
})
})
beforeAll(()=>{
console.log('begin')
})
afterAll(()=>{
console.log('finish')
})
afterEach(()=>{
console.log('每一次后')
})
beforeEach(()=>{
console.log('每一次前')
})
写完后在控制台运行yarn test,可以看到console.log的顺序和结果并没有改变。并且每一个beforeEach和afterEach也都在每一个测试用例的前后执行了,同时钩子函数在同级的describe分组里是互不干扰的,且外部的钩子函数是优先与内部执行的,具体结果如下:
阶段性结语
关于Jest的基础了解就到这里结束了,后续如果在项目中遇到更高级的用法或有空余时间的话,会继续其他知识,并在此页面下继续更新的,例如Mock技巧,快照功能,定时器,延时器,Dom节点测试等等。