1.如何使用
Jest 可以通过 npm 或 yarn 进行安装。以 npm 为例,既可用 npm install -g jest 进行全局安装;也可以只局部安装、并在 package.json 中指定 test 脚本:
"scripts": {
"test": "jest"
}
}
接下来的实例是使用Nuxt.js框架构建demo,在demo配置时选择的测试框架时Jest
Jest 的测试脚本名形如 *.test.js,不论 Jest 是全局运行还是通过 npm run test 运行,它都会执行当前目录下所有的*.test.js 或 *.spec.js 文件、完成测试。
2.简单实例
在根目录下创建utils/sum.js
function sum(a, b){
return a + b;
}
export { sum };
test文件下添加sum.test.js
import { sum } from '~/utils/sum.js';
describe('test testObject', () => {
test('测试求和函数', () => {
expect(sum(2, 2)).toBe(4);
});
})
describe() 函数表示一组用例
test() 函数表示一个测试用例
expect() 函数返回一个期望值对象,该对象提供了大量的工具方法来做结果判定
toBe() 函数表示断言方法
在Jest官方文档中,it 是 test 的别名,二者可以通用
最后运行 yarn test 或 npm run test,将打印出下面的消息,表示测试通过了。
也可以在编辑器VScode安装Jest插件,每次保存可以看到测试是否通过
3.匹配器
3.1判断相等(toBe&toEqual)
test('2加2等于4', () => {
expect(2+2).toBe(4);
});
// 测试对象相等
test('测试对象的值', () => {
const data = {a: 1};
expect(data).toEqual({a: 1});
});
// 测试不等,相反的值
test('2加2不等于1', () => {
expect(2 + 2).not.toBe(1);
});
3.2判断真假、空值
- toBeNull 只匹配 null;
- toBeUndefined 只匹配 undefined;
- toBeDefined 与 toBeUndefined 相反;
- toBeTruthy 匹配任何 if 语句为真;
- toBeFalsy 匹配任何 if 语句为假;
3.3判断数字
- toBeGreaterThan 匹配大于某值;
- toBeGreaterThanOrEqual 匹配大于或等于某值;
- toBeLessThan 匹配小于某值;
- toBeLessThanOrEqual 匹配小于或等于某值;
3.4判断浮点数
- toBeCloseTo 解决浮点数精度带来的问题
3.5判断字符串(toMatch)
异步测试
4.1callback回调函数:done
如果你有一个fetchData(callback)的function用来获取数据,并且在获取完成的时候调用callback 函数,你想测试返回的数据是“peanut butter” ,默认情况下当fetchData执行完成的时候Jest的测试就完成了,这并不是你所期望的那样的去运行。
// Don't do this!
test('the data is peanut butter', () => {
function callback(data) {
expect(data).toBe('peanut butter');
}
fetchData(callback);
});
上面代码的问题就在于一旦fetchData完成,测试也就执行完成,然后再调用回调。
Jest提供了一种用于测试的实现方式,下面代码 done() 被执行则意味着callback函数被调用。
test('the data is peanut butter', done => {
function callback(data) {
expect(data).toBe('peanut butter');
done();
}
fetchData(callback);
});
如果 done 永远都不被调用,那么的测试将会失败,这也正是我们期望的(我们希望callback被调用,并且返回的data是我们期望的值)
4.2 返回Promise
4.2.1assertions 断言
如果使用的是 promise,需要在测试中 返回 一个 promise,Jest 会自动等待 promise 被解析处理,如果 promise 被拒绝,那么测试失败。
test("Test async code with promise", () => {
// 一个rejected状态的 Promise 不会让测试失败
expect.assertions(1);
return doAsync().then((data) => {
expect(data).toBe('example');
});
});
test("Test promise with an error", () => {
// 一个fulfilled状态的 Promise 不会让测试失败
expect.assertions(1);
return doAsync().catch(e => {
expect(e).toMatch('error')
});
});
注意1:确保
返回promise,如果忽略掉 return,那么测试会在 promise 完成之前完成。
注意2:
expect.assertions(number)验证在测试期间是否调用了一定数量的断言。
同时满足以上两个条件
函数 doAsync,该函数接收两个回调 callback1 和 callback2,它将以未知顺序异步调用这两个回调。
test("doAsync calls both callbacks", () => {
expect.assertions(2);
function callback1(data) {
expect(data).toBeTruthy();
}
function callback2(data) {
expect(data).toBeTruthy();
}
doAsync(callback1, callback2);
});
使用 expect.assertions(2) 确保两个回调都实际被调用。
4.2.2resolves / .rejects Jest语法糖
// 假设 doAsync() 返回一个promise,resolve的结果为字符串'example'
it('Test async code with promise', () => {
expect.assertions(1);
return expect(doAsync()).resolves.toBe('example');
});
it('Test promise with an error', () => {
expect.assertions(1);
return expect(doAsync()).rejects.toMatch('error');
});
4.2.3async/await Promise语法糖
// 假设 doAsync() 返回一个promise,resolve的结果为字符串'example'
it('Test async code with promise', async () => {
expect.assertions(1);
const data = await doAsync();
expect(data).toBe('example');
});
async/await 也可以和 resolves/rejects 一起使用:
// 假设 doAsync() 返回一个promise,resolve的结果为字符串'example'
it('Test async code with promise', async () => {
expect.assertions(1);
await expect(doAsync()).resolves.toBe('example');
});
});
4.3done 和 assertions 区别
done:异步回调确保测试
assertions:Promise确保测试
一般测试的时候,异步都是模拟 mock 出来的,要自己控制结束,而不是真正的异步。所以 expect.assertions 某些情况下无法替代 done
5.Mock函数
在项目中,一个模块的方法内常常会去调用另外一个模块的方法。在单元测试中,我们可能并不需要关心内部调用的方法的执行过程和结果,只想知道它是否被正确调用即可,甚至会指定该函数的返回值。此时,使用Mock函数是十分有必要。
5.1jest.fn()
jest.fn()是创建 Mock 函数最简单的方式,如果没有定义函数内部的实现,jest.fn() 会返回 undefined 作为返回值。
test('测试jest.fn()调用', () => {
let mockFn = jest.fn();
let result = mockFn(1, 2, 3);
// 断言mockFn的执行后返回undefined
expect(result).toBeUndefined();
// 断言mockFn被调用
expect(mockFn).toBeCalled();
// 断言mockFn被调用了一次
expect(mockFn).toBeCalledTimes(1);
// 断言mockFn传入的参数为1, 2, 3
expect(mockFn).toHaveBeenCalledWith(1, 2, 3);
})
5.2jest.mock()
通常情况下,我们需要调用api,发送ajax请求,从后台获取数据。但是我们在做前端测试的时候,并不需要去调用真实的接口,所以此时我们需要模拟 axios/fetch 模块,让它不必调用api也能测试我们的接口调用是否正确。
5.3jest.spyOn()
jest.spyOn() 方法创建一个mock函数,并且可以正常执行被spy的函数。 jest.spyOn() 是 jest.fn() 的语法糖,它创建了一个和被spy的函数具有相同内部代码的mock函数。
6.Jest钩子函数
6.1执行顺序和作用域
钩子函数是指在某一时刻,jest会自动调用的函数。如下:
- beforeAll:在所有测试用例执行之前执行
- afterAll:等所有测试用例都执行之后执行 ,可以在测试用例执行结束时,做一些处理
- beforeEach:每个测试用例执行前执行,可让每个测试用例中使用的变量互不影响,因为分别为每个测试用例实例化了一个对象
- afterEach:每个测试用例执行结束后,做一些处理
注意:钩子函数的作用域为: 所在的 describe 分组;
6.2describe中的基础代码执行顺序
6.3only和skip
test.only(name, fn)
describe.only(name, fn)
only 只对单个测试用例进行测试
test.skip(name, fn)
describe.skip(name, fn)
skip 跳过某个测试用例进行测试