前言
虽然单元测试是暖暖的,但没人点赞,我的心是冰冰的
本篇文章为此次VUE单元测试之旅的“终点站”,请各位乘客有序下车并点赞👍!
想要成为VUE
单元测试的大师,怎么可能少的了VUEX
的测试!这篇文章主要是介绍如何对vuex
和mixin
写单元测试。
如果还有小伙伴还没写过单元测试,请移步到我之前的文章:
测试VUEX
其实测试VUEX
是有两种方法:
- 第一种就是对
VUEX
的store
组成部分分别进行测试。 - 第二种就是将它们组合到一个
store
实例上,然后测试该实例。
第一种方法的好处是简单,因为都是JavaScript
函数。所以下面主要来介绍一下第二种方法(其实也挺简单的)
单元测试主要是提供输入和断言输出。在VUEX
中的体现就是:mutation
和action
是store
的输入,getter
和state
是store
的输出,这样看就很清晰了。
先来看一个例子:
test('commit a increment, state.count well plus 1', () => {
Vue.use(Vuex)
const store = new Vuex.Store(storeConfig)
expect(state.state.count).toBe(0)
store.commit('increment')
expect(store.state.count).toBe(1)
}
看起来也是测试一个简单的JavaScript
函数一样,没啥不同的。但是别高兴的太早,因为这个例子犯了一个错,可能是前端都犯过的错,对象引用!
一个store
中的state
对象是对store
配置对象中定义的state
对象的引用。store.state
的任何更更改都会改变store
配置的state
。像上面例子一个,在测试中改变了state.count
的值,那么在store
配置的state.count
也将会改变。
这是单元测试的大忌,因为进行单元测试从而更改了程序的运行。解决方案也简单,就是clone
一个新的store
配置对象。
Vue Test Utils
有一个createLocalVue
的API
,它可以创建一个localVue
构造函数。localVue
构造函数是一个从Vue
基础构造函数扩展而来的VUE
构造函数,可以在其安装插件而不会影响Vue
基础构造函数。
测试VUEX实例
创建一个store module
。功能非常简单
- 使用
actions
发出一个fetchAlarmDetail
请求,得到alarm
的名字 - 然后在
commit
一个changeAlarmName的mutation
,将结果写在state.alarmName
上。
import { fetchAlarmDetail } from '../api/alarmApi'
export const Alarm = {
namespaced: true,
state: {
alarmName: ''
},
mutations: {
changeAlarmName (state, data) {
state.alarmName = data;
},
},
actions: {
fetchAlarmName ({commit}) {
return fetchAlarmDetail().then(res => {
commit('changeAlarmName', res)
})
}
}
}
fetchAlarmDetail
功能如下:
export const fetchAlarmDetail = () => {
return new Promise((resolve, reject) => {
try {
setTimeout(() => {
resolve('人机')
})
} catch (error) {
reject(error)
}
})
}
创建一个store.spec.js
测试
// 模拟api
jest.mock('../../src/api/alarmApi.js')
// 创建localVue构造函数
const localVue = createLocalVue()
// 在localVue构造函数上安装Vuex
localVue.use(Vuex)
describe('storeConfig', () => {
test('dispatching fetchAlarmName to update alarmName', async () => {
expect.assertions(2);
// 复制Alarm的store module
const cloneAlarm = cloneDeep(Alarm)
const store = new Vuex.Store({
modules: {
cloneAlarm
}
})
expect(store.state.cloneAlarm.alarmName).toBe('')
// 实现fetchAlarmDetail接口,并返回模拟数据
alarmApi.fetchAlarmDetail.mockImplementationOnce(() => Promise.resolve('人机'));
// dispatch actions
store.dispatch('cloneAlarm/fetchAlarmName')
await flushPromises()
expect(store.state.cloneAlarm.alarmName).toBe('人机')
})
})
运行yarn test:uni
,通过!
测试组件中的VUEX
当然啦,不在组件使用VUEX
,那么VUEX
就毫无意义。
当组件中使用VUEX
的store
时,该store
就变成了组件的一个依赖。通过Vuex
和模拟数据创建一个store
实例就能测试了
在组件中使用dispatch
一个action
// home.vue
async handleAlarmName () {
this.$store.dispatch('Alarm/fetchAlarmName').then(res => {
this.alarmName = res;
})
},
在测试中
test('call handleAlarmName to update the this.alarmName', async () => {
// 定义Alarm,将在每一个测试之前被重新分配
const Alarm = {
namespaced: true,
actions: {
// 设置一个mock fetchListData actions
fetchListData: jest.fn(() => Promise.resolve())
}
}
const store = new Vuex.Store({
modules: {
Alarm
}
})
expect.assertions(1);
store.dispatch = jest.fn(() => Promise.resolve('人机'))
// 挂载实例,并注入store与localVue
const wrapper = shallowMount(Home, { localVue, store })
wrapper.vm.handleAlarmName();
await flushPromises()
expect(wrapper.vm.alarmName).toBe('人机')
})
完美
测试mixin
为mixin
编写测试其实很简单。在代码中注册mixin
,挂载组件,然后检测mixin
是否产生了预期的行为即可。
编写mixin
export const titleMixin = {
mounted() {
document.title = 'hello mixin'
},
}
功能很简单,挂载之后将document.title
改为hello mixin
test('import titleMixin to change the title', () => {
const component = {
render() {},
title: 'hello',
// 注册mixin
mixins: [titleMixin]
}
mount(component)
expect(document.title).toBe('hello mixin')
})
结尾
好了,本次单元测试之旅到此为止。如果觉得有帮助的请点点赞,支持一下。
更多文章请移步楼主github,如果喜欢请点一下star,对作者也是一种鼓励。