持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天,点击查看活动详情
vue3常用的几个测试框架
- jest
- mocha
- vue-test-utils -官方工具
他们都包含几大功能
- 断言
- 异步支持
- 代码覆盖率
- 单元测试
我们从实践中看如何选择合适的测试方案 先从基础开始
本篇是jest基础,然后是vue-test-utils基础,最后一篇结合项目看一下怎么做项目中的实际测试
起步
安装jest和type 包(方便vscode 代码提示)
pnpm add jest --save-dev
pnpm add @types/jest --save
新建.test.js文件
//最简单的示例
it('test add', () => {
expect(2 + 2).toBe(4);
});
执行jest
// 默认会搜索所有.test.js 的文件进行执行
npx jest // 没有全局安装的情况下,如果全局安装直接jest
npx jest src/a.test.js --watchAll //监听文件改动并执行
测试
测试异步
- 回调方法方式
执行回调后需要调用done方法
const fetchData = (cb) => {
setTimeout(() => {
cb('hello');
}, 200);
};
it('test callBack', (done) => {
fetchData((data) => {
expect(data).toBe('hello');
done();
});
});
- promise方式
const usePromise = () => Promise.resolve('hello');
it('test promise', () => {
return usePromise().then((data) => {
expect(data).toBe('hello');
});
});
//或者
it('test expect', () => {
return expect(usePromise()).resolves.toBe('hello');
});
- async 方式
const usePromise = () => Promise.resolve('hello');
it('test async', async () => {
const data = await usePromise();
expect(data).toBe('hello');
});
mock
测试方法的执行
function mockTest(tickCallBack, cb) {
if (tickCallBack) {
cb(11);
}
}
t('test mock fn', () => {
const mockCb = jest.fn(); //定一个方法监听器
mockTest(true, mockCb);
expect(mockCb).toHaveBeenCalled(); // 测试函数是否被执行
expect(mockCb).toHaveBeenCalledWith(11); // 测试函数的参数是否是1
expect(mockCb).toHaveReturnedTimes(1); // 测试函数被调用的次数是否为1次
});
mock第三方库
比如我们想测试一个请求,查看请求返回和请求链接,当然使用的是我们封装的请求方式,但是又不想真实的发出请求,因为有很多的不确定因素,当然也不能修改原来封装的方法,所以我们可以直接mock axios 工具,然后模拟返回
前置工作
- 新建json文件然后安装json-server 启动一个json服务,也可以使用jsonplaceholder模拟一个远程json。
- 封装一个axios方式模拟,项目里用就直接用项目里的,这里是做测试
- 写测试用例
/* eslint-disable @typescript-eslint/no-var-requires */
const axios = require('axios');
function getTestData() {
return axios.get('http://localhost:3000/userMsg').then((res) => {
return res.data.data;
});
}
it('test axios req', (done) => {
getTestData().then((res) => {
console.log(res.name);
done();
});
});
此时没有做任何处理,请求是真正会被发出去
mock axios
jest.mock('axios');
axios.get.mockResolvedValue({ data: { data: { name: '被mock后的数据' } } });
此时再打印返回,就可以看到,name已经被修改,真正的请求也不会发出
同样也可以测试,axios方法的执行
it('test axios req', (done) => {
getTestData().then((res) => {
console.log(res.name);
expect(axios.get).toHaveBeenCalled();
expect(axios.get).toHaveBeenCalledTimes(1);
done();
});
});
或者不想每个测试文件 都写一份,或者防止漏写,
可以在根目录下创建__mocks__ 文件,然后对包进行覆写
要和node_mnodules 同级,并且里面文件名要和引入包名一致
然后里面创建文件,比如创建axios包的覆写文件
const axios = {
get: jest.fn(() => {
return Promise.resolve({
data: { data: { name: '__mocks__文件夹里面修改的数据' } },
});
}),
};
module.exports = axios;
然后.test.js里面删除对axios的mock
/* eslint-disable @typescript-eslint/no-var-requires */
const axios = require('axios');
// 不需要再mock 直接走__mocks__ 里面的覆写操作
// jest.mock('axios');
// axios.get.mockResolvedValue({ data: { data: { name: '被mock后的数据' } } });
function getTestData() {
return axios.get('http://localhost:3000/userMsg').then((res) => {
return res.data.data;
});
}
it('test axios req', (done) => {
getTestData().then((res) => {
console.log(res.name);
expect(axios.get).toHaveBeenCalled();
expect(axios.get).toHaveBeenCalledTimes(1);
done();
});
});
测试Timer
对setTimeout 和setInvertal的测试
常用的API:
- jest.useFakeTimers(); // 对时间进行代理
- jest.runAllTimers(); 所有时间执行结束后执行后续
- jest.runOnlyPendingTimers(); //执行一次时间器后执行
- jest.advanceTimersByTime(2000); // 根据时间细粒度控制
- 单次
const fetchData = (cb) => {
setTimeout(() => {
cb('hello');
}, 1000);
};
jest.useFakeTimers();
it('test', () => {
const cbFn = jest.fn();
fetchData(cbFn);
jest.runAllTimers();
expect(cbFn).toHaveBeenCalledTimes(1);
});
- 多次
const secondTimeOut = (cb) => {
setTimeout(() => {
cb('one');
setTimeout(() => {
cb('second');
}, 1000);
}, 2000);
};
it('test second timeOut', () => {
const cbFn = jest.fn();
secondTimeOut(cbFn);
jest.runOnlyPendingTimers();// 第一次时间器执行后
expect(cbFn).toHaveBeenCalled();
expect(cbFn).toHaveBeenCalledTimes(1);
jest.runOnlyPendingTimers();//第二次时间器执行后
expect(cbFn).toHaveBeenCalledTimes(2);
});
- 按照时间控制
it('test second time advance', () => {
const cbFn = jest.fn();
secondTimeOut(cbFn);
jest.advanceTimersByTime(2000);// 两秒后往后执行
expect(cbFn).toHaveBeenCalled();
expect(cbFn).toHaveBeenCalledTimes(1);
jest.advanceTimersByTime(1000); //再过一秒后执行
expect(cbFn).toHaveBeenCalledTimes(2);
});