相关文章
1. expect 的用法及 ”匹配器“
- expect 用来检查某个值是否符合预期,匹配器则是匹配的方法。
- expect.not 用来检查某个值是否不匹配结果
- expect.resolves 用来结构 promise ,从而判断其返回值(注意:这种 expect 需要在 test 中 return expect; 或者使用 async await )
- expect.rejects 同上用来判断 promise reject 后的结果
- expect.assertions(number) 用来验证在测试期间调用了一定数量的断言,通常用在测试异步代码中
常用匹配器
test('Test Matcher ', async () => {
await expect(Promise.resolve('some result'))
.resolves.toBe('some result'); // .resolves 用来解构判断 promise 的返回值
expect(2 + 2).not.toBe(5); // .not 检查某个值不匹配结果
expect(2 + 2).toBe(4); // 数值比较
expect(2.1 + 2.2).toBeCloseTo(4.3); // 浮点值比较
expect(20).toBeGreaterThan(10); // 大于
expect(20).toBeLessThan(30); // 小于
expect(null).toBeNull(); // 是否为 null
expect(undefined).toBeUndefined(); // 是否为 undefined
expect(null).toBeDefined(); // undefined 的相反值 null 也算 defined
expect([]).toBeTruthy(); // 可以使 if 语句为真的结果
expect('').toBeFalsy(); // 可以使 if 语句为假的结果
expect('xxxsss').toMatch(/^[a-z]+$/i); // 匹配字符串符合正则规则
expect([{a: 1}]).toEqual([{a: 1}]); // 对象数组等引用类型判断相等
expect(['AAA', 'BBB']).toContain('AAA'); // 判断数组是否包含
expect(() => {
throw new Error('test error')
}).toThrow('test error'); // 判断是否抛出了错误
});
// callback 异步代码,通过参数 done 来控制测试是否完成
test('Test Async Code -- Callback', (done) => {
function callback(data: string) {
try {
expect(data).toBe('peanut butter');
done(); // 必须使用 done 来控制测试执行完成,否则测试在执行 fetchData 时就会结束
} catch (error) {
done(error);
}
}
fetchData(callback);
})
test('Test Async Code -- Async', async () => {
await expect(fetchData()).resolves.toBe('peanut butter');
await expect(fetchData()).rejects.toMatch('error');
});
test('Test Async Code -- Promise', async () => {
// 由于断言在 promise 的 catch 中,不一定会执行到,而如果执行不到则代表测试应该失败,可以通过 expect.assertions 来实现在没有执行指定数量断言后的报错
expect.assertions(1);
return fetchData().catch(e => expect(e).toMatch('error'));
});
更多 expect 及 匹配器使用查看
2. Mock 函数
jest 提供了 mock 函数,可以用来帮助捕获函数的调用以及在调用过程中的传参, 允许测试时配置返回值,可以用来做假数据
import axios from 'axios';
import Utils from './utils';
// 模拟 axios 模块请求
jest.mock('axios');
const axiosReq = axios as jest.Mocked<typeof axios>;
const utils = new Utils();
describe('测试 Mock 函数', () => {
test('捕获函数调用', async () => {
utils.init(5, 1000);
/**
* utils.useCallbackFn 是一个需要传入回调函数的方法
* 通过 jest.fn 包装 我们来创建一个符合 utils.useCallbackFn 的回调函数,
* 这个回调函数除了普通函数的功能外,还具有记录回调调用情况的能力
* */
const mockFn = jest.fn((num: number) => num * 10);
utils.useCallbackFn(mockFn as unknown as (() => any));
/**
* mockFn.mock
* - calls 包含所有对此模拟函数的调用参数数组
* - results 包含所有对此模拟函数的返回值数组
* - instance 包含所有对此模拟函数的实例对象数组(通过 new 创建的实例)
* */
expect(mockFn.mock.calls.length).toBe(2); // 检查回到调用几次
expect(mockFn.mock.calls[0][0]).toBe(5); // 检查第一次调用的参数
expect(mockFn.mock.results[0].value).toBe(50); // 检查第一次调用时的返回值
const MockClass = jest.fn();
const a = new MockClass();
expect(MockClass.mock.instances[0]).toBe(a); // 检查通过 mockClass 创建的第一个实例
/**
* 除了上述通过 mockFn.mock 还可以直接使用 matchers 匹配器来进行验证
* */
expect(mockFn).toHaveBeenCalledTimes(2); // 调用几次
expect(mockFn).toHaveNthReturnedWith(1, 50); // 第一次调用返回值
// 模拟返回数据
const mockFn2 = jest.fn();
mockFn2.mockReturnValue(90);
expect(mockFn2()).toBe(90);
// 模拟 axios 模块 请求后返回的数据
axiosReq.get.mockResolvedValue({data: [90, 200]});
// utils.getData 中使用了 axios 请求数据
await expect(utils.getData()).resolves.toEqual({data: [90, 200]})
})
})
查看 其他 mock 函数相关 API
3. jest 配置文件
配置文件
- 执行 jest --init ,回答几个问题后就会生成 jest.config.(js|ts) 配置文件
- 除此之外也可以将配置加载 package.json 的 jest 字段上
- jest.config.(js|ts) 文件中可以 export 一个配置对象,或者是一个 async function
// jest.config.ts
export default {
// 在 n 次错误之后停止进行剩下的 test 文件
bail: 1,
// 是否在测试时收集覆盖率数据,开启后会降低测试速度
collectCoverage: true,
// 覆盖率报告的存放地址
coverageDirectory: "coverage",
// 定义收集测试覆盖率的文件来源
collectCoverageFrom: ['src/components/**', '!**/node_modules/**', '!src/**/*.d.ts'],
// 列举 jest 收集测试文件的目录,默认是 <rootDir> 也就是配置文件所在的目录
roots: [
"<rootDir>/src/components" // 例:只收集 /src/components 下的测试文件
],
// 那些文件会被当做测试文件,默认是 __tests__ 下的 js 以及 命名为 xxx.test.js 的文件
testMatch: [ "**/__tests__/**/*.[jt]s?(x)", "**/?(*.)+(spec|test).[jt]s?(x)" ]
// setupFiles 或者 setupFilesAfterEnv 会在每一个 test 文件执行前执行,且 setupFiles 会优先执行们通常用来设置一些环境
setupFiles: ['react-app-polyfill/jsdom'],
// 如果是个本地文件可以 通过 <rootDir> 来定义文件路径
setupFilesAfterEnv: ['<rootDir>/tests/setupTests.ts'],
// 测试环境 jsdom | node
testEnvironment: "jsdom",
// 类似 webpack alias 设置别名,如下设置后就可以直接引用 tests/xxx
moduleNameMapper: {
'^tests/(.*)$': '<rootDir>/tests/$1',
"\\.(css|less)$": "identity-obj-proxy" // 配置处理样式文件,如果需要自定义可以放在 transform 中实现
},
/**
用来配置 jest 如何处理文件资源
如下 fileTransformer.js 需要是一个具有如下结构的模块
module.exports = {
process(src, filename, config, options) {
return 'module.exports = ...;';
},
};
*/
"transform": {
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/fileTransformer.js"
}
}
参考: