VUE单元测试--终点

1,032 阅读4分钟

前言

虽然单元测试是暖暖的,但没人点赞,我的心是冰冰的

本篇文章为此次VUE单元测试之旅的“终点站”,请各位乘客有序下车并点赞👍!

想要成为VUE单元测试的大师,怎么可能少的了VUEX的测试!这篇文章主要是介绍如何对vuexmixin写单元测试。

如果还有小伙伴还没写过单元测试,请移步到我之前的文章:

测试VUEX

其实测试VUEX是有两种方法:

  • 第一种就是对VUEXstore组成部分分别进行测试。
  • 第二种就是将它们组合到一个store实例上,然后测试该实例。

第一种方法的好处是简单,因为都是JavaScript函数。所以下面主要来介绍一下第二种方法(其实也挺简单的)

单元测试主要是提供输入和断言输出。在VUEX中的体现就是:mutationactionstore的输入,getterstatestore的输出,这样看就很清晰了。

先来看一个例子:

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有一个createLocalVueAPI,它可以创建一个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就毫无意义。

当组件中使用VUEXstore时,该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,对作者也是一种鼓励。