Jest 简介
Jest是Facebook开源的一套JavaScript测试框架, 它集成了断言、JSDom、覆盖率报告等开发者所需要的所有测试工具。
Jest基本环境搭建
项目搭建
这里我们使用vite
提供的一套社区模板直接搭建我们的项目,基于react + vite
# 使用git克隆模板
git clone https://github.com/SafdarJamal/vite-template-react.git
# 进入我们的项目
cd vite-template-react
# 安装依赖
yarn
# 启动项目
yarn dev
安装Jest 和 初始化Jest配置文件
# 安装
yarn add jest
# 执行该命令会有一系列选项说明给你填,基本我们点确认就行了,
# 执行完后会在项目根项目生成jest.config.js 配置文件
npx jest --init
简单案例测试
我们在src
的目录下新建一个utils
的目录,在下面新建一个calc.js
的文件夹,在里面我们新建两个简单的加减的函数
function addSum(num1, num2) {
return num1 + num2;
}
function reduceNum(num1, num2) {
return num1 - num2;
}
module.exports = {
addSum,
reduceNum
}
然后我们在src
的根目录下新建一个App.test.js
文件,这是jest
文件的格式,我们使用jest
的测试方法来模拟测试调用calc.js
里面的两个函数返回的结果是否通过
// App.test.js
const { addSum, reduceNum } = require('./utils/calc')
test("测试:10+10 = 20", () => {
expect(addSum(10,10)).toBe(20)
})
test('测试:10-8= 2', () => {
expect(reduceNum(10,8)).toBe(2)
});
然后我们运行我们的jest
测试,你可以使用npx jest
命令也可以在package.json
文件在script
新增一条命令,方便我们运行测试
测试结果
这里故意将写成10-8
的结果写成1,所以我们的符合预期的结果就是有addSum
计算的结果通过,而reduceNum
计算的结果不通过,当我们运行yarn test
时,jest就会自动帮我们运行测试用例并返回结果
测试覆盖率生成
我们直接运行yarn coverage
命令(package.json里有配置),在我们的目录下会生成一个coverage
的文件夹,而且这个文件夹名称是可以自定义配置的,如果是多个人同事开发的话可以自定义自己的名称,我们只需要在jest.config.js
中配置
然后在我们目录下会生成coverage
的一个目录,我们点进去找到lcov-report
下的index.html
文件,用浏览器打开就会看到测试报告
jest 中常用的匹配器
{% note info green %}
toBe()
: 完全相等,精确匹配
toEqual()
: 值相等匹配
{% endnote %}
toBe()
test('toBe()匹配器', () => {
// 完全相等匹配
const a = { test: 1 };
const b = a
// expect(a).toBe({test:1}) // 不通过
expect(a).toBe(b) // 通过
});
toEqual()
test('toEqual()匹配器', () => {
// 值相等匹配
const a = {name:'test'};
// expect(a).toBe({name:'test'}) // 不通过
expect(a).toEqual({name:'test'}) // 通过
});
{% note info green %}
toBeNull()
: 仅匹配null
toBeUndefined()
: 仅匹配未定义
toBeDefined()
: 仅匹配定义的
toBeTruthy()
: 匹配为true的内容
toBeFalsy()
: 匹配为false的内容
{% endnote %}
toBeNull()
test('toBeNull()匹配器', () => {
// 仅匹配null值
const a = null
expect(a).toBeNull() // 通过
});
toBeUndefined()
test('toBeUndefined()匹配器', () => {
// 仅匹配undefined值
const a = undefined
expect(a).toBeUndefined () // 通过
});
toBeDefined()
test('toBeDefined()匹配器', () => {
// 有值就通过
const a = ""
expect(a).toBeDefined () // 通过
});
toBeTruthy()
test('toBeTruthy()匹配器', () => {
// 仅匹配真值
const a = true
expect(a).toBeTruthy() // 通过
});
toBeFalsy()
test('toBeFalsy()匹配器', () => {
// 仅匹配假值
const a = false
expect(a).toBeFalsy() // 通过
});
{% note info green %}
toBeGreaterThan()
: 大于
toBeGreaterThanOrEqual()
: 大于等于
toBeLessThan()
: 小于
toBeLessThanOrEqual()
: 小于等于
toBeCloseTo()
: 专门用于匹配两个浮点数是否相等,如果用toEqual()
会造成误差
{% endnote %}
toBeGreaterThan()
test('toBeGreaterThan()匹配器', () => {
// 大于
const a = 0
expect(a).toBeGreaterThan(-1) // 通过
});
toBeGreaterThanOrEqual()
test('toBeGreaterThanOrEqual()匹配器', () => {
// 大于等于
const a = 0
expect(a).toBeGreaterThanOrEqual(0) // 通过
});
toBeLessThan()
test('toBeLessThan()匹配器', () => {
// 小于
const a = 0
expect(a).toBeLessThan(1) // 通过
});
toBeLessThanOrEqual()
test('toBeLessThanOrEqual()匹配器', () => {
// 小于等于
const a = 0
expect(a).toBeLessThanOrEqual(0) // 通过
});
toBeCloseTo()
test('toBeCloseTo()匹配器', () => {
// 解决浮点数出现精度问题
const a = 0.1 + 0.2;
// expect(a).toEqual(0.3) // 不通过
expect(a).toBeCloseTo(0.3) // 通过
});
{% note info green %}
toMatch()
: 根据字符串或者正则表达是否匹配
{% endnote %}
toMatch()
test('toMatch()匹配器', () => {
// 根据字符串或者正则表达是否匹配
const a = 'Christoph';
expect(a).toMatch('stop');
expect(a).toMatch(/stop/);
});
{% note info green %}
toContain()
: 根据字符串或者正则表达是否匹配
{% endnote %}
toContain()
test('toContain()匹配器', () => {
// 匹配数组是否包含某一项值
const a = ['test1', 'test2', 'test3'];
// 也可以匹配Set
const newArr = new Set(a);
expect(a).toContain('test'); // 不通过
expect(a).toContain('test1'); // 通过
expect(newArr).toContain('test1'); // 通过
});
{% note info green %}
toThrow()
: 测试某个特定函数在调用时是否抛出错误
{% endnote %}
toThrow()
test('toThrow()匹配器', () => {
// 匹配异常
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支持ES6
写法
Jest
默认不支持ES6的写法,当我们导入一个文件时必须是用require()
到导入,导出使用module.export = {...}
,
要想Jest
支持import
的写法必须要安装babel
转换相关的插件:@babel/core
和 @babel/preset-env
,安装好这两个插件后,然后在根目录下新建一个.babelrc
文件,然后在里面写上下面的配置,完成后我们就可以使用import
相关的写法了
// .babelrc 配置
{
"presets": [
[
"@babel/preset-env",{
"targets":{
"node":"current"
}
}
]
]
}
异步代码测试的几种方式
接口模拟
直接新建一个data.json
文件,然后通过axios
请求
{
"success":true
}
请求接口
// 第一种方式
export function fetchOneData(fn) {
axios.get('http://localhost:3000/data.json').then(res => {
// console.log(res.data,'res......')
fn(res.data)
})
}
// 第二种方式
export function fetchTwoData() {
return axios.get('http://localhost:3000/data.json')
}
// 第三种方式
export function fetchThreeData() {
return axios.get('http://localhost:3000/data.json')
}
异步测试(一)
{% note info green %}
使用done
参数,Jest
将会等待知道调用done
回调,然后再完成测试
{% endnote %}
// fetch.test.js
//第一种,使用done完成异步函数的测试
test('加done', (done) => {
// 错误!不加done的话,就算接口不存在,这个函数一样是通过的
fetchOneData(res => {
expect(res).toEqual({ success: true })
})
// 正确
fetchOneData(res => {
expect(res).toEqual({ success: true })
done() // 必须要加done才能正确通过异步函数测试
})
});
异步测试(二)
{% note info green %}
使用return
,如果接口返回的是一个Promise
对象的话,return
会等待Promise
返回然后再完成测试
{% endnote %}
// fetch.test.js
//第二种,使用return完成异步函数的测试
test('加return', () => {
// 错误!不加done的话,就算接口不存在,这个函数一样是通过的
fetchTwoData().then(res => {
expect(res.data).toEqual({
success: true
})
})
// 正确
return fetchTwoData().then(res => {
expect(res.data).toEqual({
success: true
})
})
});
异步测试(二)
{% note info green %}
使用 Async/Await
{% endnote %}
// fetch.test.js
//第三种,使用Async/Await
test('写法1', async () => {
const res = await fetchThreeData();
expect(res.data).toEqual({success:true})
});
test('写法2', async () => {
// 断言,必须执行一次expect
// 在使用catch处理异常的时候,必须需要使用断言,不然`catch`里面的代码是不会执行的
expect.assertions(1);
try {
await fetchData();
} catch (e) {
expect(e).toMatch('error');
}
});
test('写法3', async () => {
await expect(fetchData()).resolves.toEqual({success:true});
await expect(fetchData()).rejects.toMatch('error');
});