我们在测试的时候通常项目组件中依赖了各种各样的插件等外部依赖,需要全部倒入才能构成可执行环境,这里记录一下,单测这个东西几天不写就丢了,方便自己复习和使用
import {
shallowMount,
createLocalVue,
} from '@vue/test-utils';
// 导入被测的组件
import Component from '@/components/Component';
// 导入路由
import VueRouter from 'vue-router';
// 导入vuex和你的store实例
import Vuex from 'vuex';
import store from '@/store/index.js';
// 导入elementui
import ElementUI from 'element-ui';
// 创建临时Vue实例,用于注册插件(elementui),避免污染组件实例的数据
const localVue = createLocalVue();
// 实例化路由
const router = new VueRouter();
// 提前准备好你要mock的数据,或者从外部文件导进来也可
const list = 你的mock数据;
const userList = 你的mock数据;
/**
* @author Suk
* @priority P0
* @casetype unit
*/
describe('测试Component组件', () => {
// 在每个测试用例启动之前执行
beforeAll(() => {
// mock掉API数据
Object.keys(window.API).forEach(apiMethod => {
// 先给所有的api固定返回一个结果,保证组件中调用data数组api时不报错
window.API[apiMethod] = () => Promise.resolve({
data: [],
});
// 测试跑到哪个api时在这里给其mock具体的数据
switch (apiMethod) {
case 'list':
window.API[apiMethod] = () => Promise.resolve({
data: list // 把假数据返回,组件里调用时就是用的这个数据
});
break;
case 'user':
window.API[apiMethod] = () => Promise.resolve({
data: userList
});
break;
}
});
// mock app.$axios 解决报错app is not defined
window.app = {
$axios: () => Promise.resolve({
data: []
}),
}
});
// 每个测试用例执行完毕后执行 可以做一些重置动作 避免用例之间数据互相影响
afterEach(() => {
// todo...
});
// 对应的还有afterAll beforeEach
it('测试store注入', () => {
// 使用shallowMount只会挂载当前组件,子组件都不会被实例化 尽量不用mount,子组件也会被挂载 如果需要使用ref拿子组件时可以用一下
const wrapper = shallowMount(Component, {
store, // 这里填入你的store实例(new Vuex.Store()实例化后的,没有实例化会报错!这里的store是在外部文件实例化过的)
localVue, // 填入临时Vue实例
router, // 填入路由
// 这里可以填组件所依赖的各种Vue实例的配置项 包括mixins,extends,propData,filters,data等等
});
const {
vm,
} = wrapper;
// 这样就能使用你的store数据了
expect(vm.$store.state.moduleName.xxx).toStrictEqual('helloWorld in Vuex~');
});
});
对应组件中API要转换一下写法,不然单测里是访问不到直接导入进来的API对象的,我们也没法在单测里mock掉api,这里解决组件中API.xxx这种写法的mock方案来自StackOverflow的一位大佬,没有传送门,提一下
// 直接导出一个API对象
export default {
user() {
return app.$axios({
url: '/user',
method: 'GET',
});
},
list() {
return app.$axios({
url: '/list',
method: 'GET',
});
},
};
// 这里的app.$axios是在window上挂载了一个app的全局变量,然后将axios挂载上去才能这么写,因项目而异不是写死的
组件中API要这么写:
import API from './api';
// 将api挂载到全局window对象上 这样我们在单测中才能去mock掉API,使用假数据返回
window.API = API;
// 在methods中返回:
methods: {
getAPI() {
return window.API;
}
}
// 调用处则通过实例访问
await this.getAPI().user();
await this.getAPI().list();
如果需要读写vuex的数据,直接操作就行,不需要走mutation那一套规范,否则可能会操作失败数据没有变化
const wrapper = shallowMount(Component,{...});
const {vm} = wrapper;
// 直接修改
vm.$store.state.moduleName.xxx = yyy;
// 直接读取
vm.$store.state.moduleName.xxx;
watch中改了属性值想测试,有时候是watch对象中的属性,可能这个对象还不是data中的,是父组件传进来的或者是extends/mixins等注入进来的,写单测时是没有这个对象的,需要使用$set改值才会触发watch:
it('测试watch', () => {
const wrapper = shallowMount(Component,{...});
const {vm} = wrapper;
// 组件挂载完成后修改属性值,触发watch:
vm.$nextTick(() => {
vm.$set(vm, 'data', {
xxx: 123,
});
// 修改后等到渲染完成后再断言是否修改成预期值
vm.$nextTick(() => {
expect(vm.data.xxx).toStrictEqual(123);
});
});