前言
虽然单元测试是暖暖的,但没人点赞,我的心是冰冰的
本篇文章为此次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)
}
2
3
4
5
6
7
8
看起来也是测试一个简单的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)
})
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
fetchAlarmDetail功能如下:
export const fetchAlarmDetail = () => {
return new Promise((resolve, reject) => {
try {
setTimeout(() => {
resolve('人机')
})
} catch (error) {
reject(error)
}
})
}
2
3
4
5
6
7
8
9
10
11
12
创建一个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('人机')
})
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
运行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;
})
},
2
3
4
5
6
在测试中
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('人机')
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
完美
测试mixin
为mixin编写测试其实很简单。在代码中注册mixin,挂载组件,然后检测mixin是否产生了预期的行为即可。
编写mixin
export const titleMixin = {
mounted() {
document.title = 'hello mixin'
},
}
2
3
4
5
功能很简单,挂载之后将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')
})
2
3
4
5
6
7
8
9
10