明白要测试的是什么,只关注输入输出
一、新建测试文件
jest 支持三种方式写测试代码
- 以 .spec.js 命名
- 以 .test.js 命名
- 放在 __tests__文件夹下
二、规划测试场景
√ 检查索要发票首页-列表父组件初始化
√ 检查索要发票首页-列表父组件:tagID=0,验证获取可申请开票数据成功并加载
√ 检查索要发票首页-列表父组件:tagID=0,验证获取可申请开票数据失败
√ 检查索要发票首页-列表父组件:tagID=1,验证获取开票中数据成功并加载
√ 检查索要发票首页-列表父组件:tagID=1,验证获取开票中数据失败
√ 检查索要发票首页-列表父组件:tagID=2,验证获取完成开票数据成功并加载
√ 检查索要发票首页-列表父组件:tagID=2,验证获取完成开票数据失败
三、构建测试代码结构
import {shallowMount} from '@vue/test-utils';
import '@/test/component/mobile';//RequestInvoice_TabList使用了vant组件
import RequestInvoice_TabList from '../../../pages/inv/invoiceCenter/mobile/RequestInvoice_TabList.vue';
shallowMount将会创建一个包含被挂载和渲染的Vue组件的Wrapper,只存根当前组件,不包含子组件(mount:会测试子子孙孙组件,耗费性能更大。一般用 shallowMount 即可。)。
const common = require('@teld/api-proxy/src/utils/common.js'); //注册公共组件库
const url = require('@teld/api-proxy/src/utils/url.js');
const $utils = {
common: common,
url:url,
sgApi: {
getDataAsync: (param) => {
//可以根据param参数,写入你期望的返回值
console.log("mock 成功")
}
}
};
我们的代码中用到了this.$utils相关,需要引入,避免单元测试运行时报错
describe('RequestInvoice_TabList.vue', () => {
let $route;
let mockEventBus;
// 创建一个模拟的 EventBus
mockEventBus = {
$on: jest.fn(),
$off: jest.fn(), // 你可能还需要在组件销毁时调用这个来清理事件监听器
$emit: jest.fn(),
};
const updateTagID = jest.fn();
beforeEach(() => {
$route = {
query: {CompanyID:"B2D7B9B6-E936-46BA-8B8B-0B83C1AFFE7C"}
}
})
const factory = (values = {}) => {
return shallowMount(RequestInvoice_TabList,
{ mocks:
{$route,
$EventBus: mockEventBus,
$utils
}},
)
}
it('检查索要发票首页-列表父组件初始化', async() => {
const wrapper = factory();
expect(wrapper.exists()).toBe(true);
expect(wrapper.vm.companyID).toBe("B2D7B9B6-E936-46BA-8B8B-0B83C1AFFE7C"); // 检查 companyID 是否根据路由参数正确设置
wrapper.vm.onLoad();
await wrapper.vm.$nextTick(); // 等待
expect(wrapper.vm.loading).toBe(false);
});
it('检查索要发票首页-列表父组件:tagID=0,验证获取可申请开票数据成功并加载',async () => {
const wrapper = factory();
// 当点击可申请开票,传入的tagID=0时
wrapper.vm.tagID = 0;
wrapper.vm.page = 1;
wrapper.vm.finished = false;
wrapper.vm.invoiceListAll = [];
wrapper.vm.loadList();
const jsonData = require('../../testData/ISSG-GetSettleBillInvoiceList.json'); // 替换为实际的文件路径
await wrapper.vm.$nextTick(); // 等待
expect(wrapper.html()).toContain('<requestinvoicetab_applyforinvoice-stub'); //检查子组件 requestinvoicetab_applyforinvoice 是否被渲染
expect(wrapper.vm.total1).toBe(13); // 检查 total1 是否正常
expect(wrapper.vm.invoiceListAll.length).toBe(jsonData.data.rows.length);
expect(wrapper.vm.invoiceListAll).toEqual(jsonData.data.rows);
const dom = wrapper.find('requestinvoicetab_applyforinvoice-stub') // 检查 渲染的子组件参数是否正确
expect(dom.attributes().companyid).toBe("B2D7B9B6-E936-46BA-8B8B-0B83C1AFFE7C");
expect(dom.attributes().tagid).toBe("0");
expect(dom.attributes().total1).toBe("13");
// expect(wrapper.element).toMatchSnapshot(); // 检查组件的渲染输出与预定义的“快照”(snapshot)相匹配
});
it('检查索要发票首页-列表父组件:tagID=0,验证获取可申请开票数据失败',async () => {
const wrapper = factory();
// 当点击可申请开票,传入的tagID=0时
wrapper.vm.tagID = 0;
const jsonData = {
"data": null,
"errcode": "",
"errmsg": "",
"state": "0",
"errstack": null
};
wrapper.vm.processInvoiceList(jsonData);
await wrapper.vm.$nextTick(); // 等待
expect(wrapper.html()).toContain('<requestinvoicetab_applyforinvoice-stub'); //检查子组件 requestinvoicetab_applyforinvoice 是否被渲染
expect(wrapper.vm.invoiceListAll.length).toBe(0);
expect(wrapper.vm.invoiceListAll).toEqual([]);
expect(wrapper.element).toMatchSnapshot(); // 检查组件的渲染输出与预定义的“快照”(snapshot)相匹配
});
it('检查索要发票首页-列表父组件:tagID=1,验证获取开票中数据成功并加载',async () => {
const wrapper = factory();
// 当点击可申请开票,传入的tagID=0时
wrapper.vm.tagID = 1;
wrapper.vm.page = 1;
wrapper.vm.finished = false;
wrapper.vm.invoiceListAll = [];
wrapper.vm.loadList();
const jsonData = require('../../testData/ISSG-GetPartnerInvoiceAppList.json'); // 替换为实际的文件路径
await wrapper.vm.$nextTick(); // 等待
expect(wrapper.html()).toContain('<requestinvoicetab_invoicingandcompleted-stub'); //检查子组件 requestinvoicetab_applyforinvoice 是否被渲染
expect(wrapper.vm.invoiceListAll.length).toBe(jsonData.data.rows.length);
expect(wrapper.vm.invoiceListAll).toEqual(jsonData.data.rows);
const dom = wrapper.find('requestinvoicetab_invoicingandcompleted-stub') // 检查 渲染的子组件参数是否正确
expect(dom.attributes().companyid).toBe("B2D7B9B6-E936-46BA-8B8B-0B83C1AFFE7C");
expect(dom.attributes().tagid).toBe("1");
// expect(wrapper.element).toMatchSnapshot(); // 检查组件的渲染输出与预定义的“快照”(snapshot)相匹配
//翻页
if(!wrapper.vm.finished){
wrapper.vm.loading = true;
}
wrapper.vm.onLoad();
await wrapper.vm.$nextTick(); // 等待
expect(wrapper.vm.page).toBe(2);
});
it('检查索要发票首页-列表父组件:tagID=1,验证获取开票中数据失败',async () => {
const wrapper = factory();
// 当点击可申请开票,传入的tagID=0时
wrapper.vm.tagID = 1;
const jsonData = {
"data": null,
"errcode": "",
"errmsg": "",
"state": "0",
"errstack": null
};
wrapper.vm.processInvoiceList(jsonData);
await wrapper.vm.$nextTick(); // 等待
expect(wrapper.html()).toContain('<requestinvoicetab_invoicingandcompleted-stub'); //检查子组件 requestinvoicetab_applyforinvoice 是否被渲染
expect(wrapper.vm.invoiceListAll.length).toBe(0);
expect(wrapper.vm.invoiceListAll).toEqual([]);
expect(wrapper.element).toMatchSnapshot(); // 检查组件的渲染输出与预定义的“快照”(snapshot)相匹配
});
it('检查索要发票首页-列表父组件:tagID=2,验证获取完成开票数据成功并加载',async () => {
const wrapper = factory();
// 当点击可申请开票,传入的tagID=0时
wrapper.vm.tagID = 2;
wrapper.vm.page = 1;
wrapper.vm.finished = false;
wrapper.vm.invoiceListAll = [];
wrapper.vm.loadList();
const jsonData = require('../../testData/ISSG-GetPartnerInvoiceAppList1.json'); // 替换为实际的文件路径
await wrapper.vm.$nextTick(); // 等待
expect(wrapper.html()).toContain('<requestinvoicetab_invoicingandcompleted-stub'); //检查子组件 requestinvoicetab_applyforinvoice 是否被渲染
expect(wrapper.vm.invoiceListAll.length).toBe(jsonData.data.rows.length);
expect(wrapper.vm.invoiceListAll).toEqual(jsonData.data.rows);
const dom = wrapper.find('requestinvoicetab_invoicingandcompleted-stub') // 检查 渲染的子组件参数是否正确
expect(dom.attributes().companyid).toBe("B2D7B9B6-E936-46BA-8B8B-0B83C1AFFE7C");
expect(dom.attributes().tagid).toBe("2");
expect(wrapper.html()).toContain('没有更多了'); // 基本 HTML 结构检查
// expect(wrapper.element).toMatchSnapshot(); // 检查组件的渲染输出与预定义的“快照”(snapshot)相匹配
//翻页
wrapper.vm.onLoad();
await wrapper.vm.$nextTick(); // 等待
expect(wrapper.vm.page).toBe(1);
expect(wrapper.html()).toContain('没有更多了'); // 基本 HTML 结构检查
});
it('检查索要发票首页-列表父组件:tagID=2,验证获取完成开票数据失败',async () => {
const wrapper = factory();
// 当点击可申请开票,传入的tagID=0时
wrapper.vm.tagID = 1;
const jsonData = {
"data": null,
"errcode": "",
"errmsg": "",
"state": "0",
"errstack": null
};
wrapper.vm.processInvoiceList(jsonData);
await wrapper.vm.$nextTick(); // 等待
expect(wrapper.html()).toContain('<requestinvoicetab_invoicingandcompleted-stub'); //检查子组件 requestinvoicetab_applyforinvoice 是否被渲染
expect(wrapper.vm.invoiceListAll.length).toBe(0);
expect(wrapper.vm.invoiceListAll).toEqual([]);
expect(wrapper.element).toMatchSnapshot(); // 检查组件的渲染输出与预定义的“快照”(snapshot)相匹配
});
});
describe(name, fn)这边是定义一个测试套件,RequestInvoice_TabList.vue是测试套件的名字,fn是具体的可执行的函数it(name, fn)是一个测试用例,检查索要发票首页-列表父组件初始化是测试用例的名字,fn是具体的可执行函数;一个测试套件里可以保护多个测试用例。beforeEach():它是Jest的钩子函数,会在执行每一个测试用例之前调用,在这个钩子函数中我们重新挂载组件,避免多个测试用例互相影响。
async/await:因为我们调用了公共方法,它修改了组件的数据,进而会触发DOM更新,因此我们需要调用组件的$nextTick()方法,以确保我们获取到了正确DOM的状态。
$route:当前匹配路由的信息,其中包含路由参数中的字段。$EventBus:模拟引入全局事件方法。- 引入工厂函数 ,将
values对象合并到了data并返回了一个新的wrapper实例。这样我们就不需要在每个测试中重复const wrapper = shallowMount(RequestInvoice_TabList)。 expect是Jest内置的断言。
toBe是Jest提供的断言方法,严格相等。- 对于异步的代码,写断言的时候需要放在
wrapper.vm.$nextTick() wrapper.html()方法,返回组件渲染后的DOM结构wrapper.attributes()方法,他返回组件渲染后的DOM属性对象- toContain :用于检查数组或字符串是否包含特定项
- toEqual :是“相等”,不是“相同”,相当于==
- toMatchSnapshot :获取代码的快照,并将其与以前保存的快照进行比较,如果新的快照与前一个快照不匹配,测试会失败。快照测试对于测试一个组件来说,相对比较有用,因为如果添加了快照测试,它能防止我们错误的修改了组件。
四、结果
从coverage\lcov-report文件夹下找到文件:RequestInvoice_TabList.vue.html,打开后可以看到目前已经覆盖和未覆盖的代码