翻译 测试 Vuex 相关的 Vue.js 组件

es6china · 2018年03月22日 · 335 次阅读

几个月前,我开始研究一个项目,需要用到大量的js。但是在代码越来越多的时候,缺乏单元测试知识的我感到很崩溃。因为没有什么办法来验证我的代码是否有bug,功能是否完善,使用手动来完成所有测试感觉很沮丧。

我们已经知道单元测试应该是最基本的,所以我开始阅读和学习相关文档。Karma, Mocha, Chai , Sinon.JS等都是很nice的,他们能提供我所需要的一切。

在测试与api、Vuex和socket交互的组件时,仍然存在一个问题。在测试Vue组件和Vuex存储之间的交互时,我遇到了一个特殊的问题。所以我决定写一个简单的例子,说明在同样的情况下,对你所知道可能有用的东西。

开始:

我将使用vue的脚手架vue-cli来对vue进行一些配置。它会支撑您的项目,最重要的是,为您配置整个测试环境。 Vue-cli会提示你需要什么样的设置。 为了这篇文章的目的,我不想用NightWatch设置端到端测试,所以你现在可以跳过这个,但是这绝对是值得研究的东西:

$ npm install -g vue-cli
$ vue init webpack my-project
$ cd my-project
$ npm install

在此之后,您可以运行该项目,看看是否一切正常: 继续:

$ npm run dev

我还建议你运行测试并确认一切正常。 不出所料:

$ npm test run

安装Vuex

从官方文档:“Vuex是Vue.js应用程序的状态管理模式+库。 它作为应用程序中所有组件的集中存储区,并且规则确保只能以可预测的方式对状态进行变更。“ 假设您在同一页面上有几个Vue组件,并且他们需要修改相同的数据,例如, 您有任务列表,用于创建新任务和修改现有任务的模式窗口,以及显示最后三个已修改任务的侧栏。

这些组件显然需要相互之间进行通信,并且有一种方法可以在没有Vuex的情况下完成此操作,例如将事件从子组件发送到父组件,并将数据传递给子组件或在Vue应用程序实例上定义数据,但这两者都有一些缺点。

这就是我们需要使用Vuex的地方,并为这些类型的问题提供了一个简单且可维护的解决方案。

为了安装它,请导航到您的项目的根目录并运行:

$ npm install --save vuex

由于我将使用使用PhantomJS的默认vue-cli Karma设置,因此我们需要使用Promise polyfill for Vuex来工作:

$ npm install --save-dev es6-promise

好的,这样做让我们有一些乐趣,并在我们的项目中包含Vuex。 在src /文件夹中添加一个具有以下目录结构的存储文件夹:

index.js ( bootstrapping our store )
mutation-types.js
actions.js
getters.js
api.js ( api service )
items.json ( our imaginary API )

Vuex只能在一个文件中设置,但如果您处理大量突变并且测试变得更加困难,它就会变得一团糟。 如果您对Vuex不熟悉,您可能需要先阅读官方文档。

src/store/index.js
require('es6-promise').polyfill()
import Vue from 'vue/dist/vue.common.js'
import Vuex from 'vuex/dist/vuex.js'

Vue.use(Vuex)

import * as types from './mutation-types'
import * as actions from './actions'
import * as getters from './getters'

const state = {
  items: []
}

const mutations = {
  [types.setItems] (state, items) {
    state.items = items
  }
}

const options = {
  state,
  mutations,
  actions,
  getters
}

export default new Vuex.Store(options)
export { options }
src/store/actions.js
import * as types from './mutation-types'
import api from './api'

export const setItems = ({commit}) => {
  api.getItems()
    .then((items) => {
      commit(types.setItems, items)
    })
    .catch((err) => console.log(err))
}
src/store/mutation-types.js
export const setItems = 'SET_ITEMS'
src/store/getters.js
export const items = state => {
  return state.items
}
src/store/api.js
export default {
  getItems: function () {
    return new Promise((resolve, reject) => {
      // imagine we're making an API request here
      const response = require('./items.json')
      resolve(response.body)
    })
  }
}
Server Response: src/store/items.json
{
  "body": [
    "coffee",
    "sugar",
    "water"
  ]
}

现在我们有了基本的Vuex设置,我们需要将它包含在我们的Vue应用程序实例中:

src/main.js
...
import store from './store'
...

new Vue({
  el: '#app',
  store, //在我们的Vue实例中使用我们新创建的例子
  template: '<App/>',
  components: { App }
})

依赖于Vuex状态的Vue组件

现在我们已经设置了Vuex存储并将其包含在我们的Vue应用程序实例中,让我们制作一个非常非常简单的使用存储状态的Vue组件。 使用以下内容创建一个新文件src/components/Items.vue

<template>
  <ul>
    <li v-for="(item, index) in items" :key="index" class="items">
      {{ item }}
    </li>
  </ul>
</template>
<script>
  import { mapActions, mapGetters } from 'vuex'
  export default {
    mounted () {
      this.setItems()
    },
    methods: {
      ...mapActions(['setItems'])
    },
    computed: {
      ...mapGetters(['items'])
    }
  }
</script>   

正如你所看到的,这里没有什么有趣的东西可以看到。 我们正在呈现来自我们商店的物品清单。 在挂载的钩子中,我们分派一个操作,从API中获取数据并将其设置为状态。 MapActions和mapGetters只是一种方便的方式,可以直接将动作和getter注入到组件中,而不是执行此操作。$store.dispatch('setItems')this.$ store.state.items

编写Items.vue组件的单元测试

那么如何去测试它呢? 显然,这个组件无法访问商店就无法测试。 所以我们需要在我们的测试中包含商店,但是我们的API呢? 我们可以按原样进行测试,但其中存在太多变量,测试环境必须纯净且每次都保持不变。

让我们一步一步来。 使用以下内容创建一个新文件test/unit/specs/Items.spec.js:

require('es6-promise').polyfill()
import Vue from 'vue/dist/vue.common.js'
import Vuex from 'vuex/dist/vuex.js'
import store from '../../../src/store'
import Items from '../../../src/components/Items.vue'

describe('Items.vue', () => {
  it('test initial rendering with api', (done) => {
    const vm = new Vue({
      template: '<div><test></test></div>',
      store,
      components: {
        'test': Items
      }
    }).$mount()

    Vue.nextTick()
      .then(() => {
        expect(vm.$el.querySelectorAll('.items').length).to.equal(3)
        done()
      })
      .catch(done)
  })
})

在这个测试中,我们正在装载一个新的Vue应用程序实例,只有我们的组件包含在它的模板中作为 </ test>。 应用程序安装完毕后,组件也会挂载,挂载的挂接程序会调度Vuex操作和后续API请求。 我们使用Vue.nextTick(),因为DOM更新是异步的,如果我们不使用它,我们的测试不会通过,因为当时DOM不会被更新。 这个测试会通过,因为我们的API只是从文件读取,但是当我们向src / store / items.json添加另一个数组项时,它将失败。

所以我们需要做的是模拟src/store/api.js服务。 为此,我们将使用注入加载器。 所以我们需要做的第一件事就是安装它:

$ npm install inject-loader@^2.0.0

现在我们已经安装了,我们可以重写我们的测试并将我们的模拟API服务注入到src / store / actions.js中。 让我们修改我们的test/unit/specs/Items.spec.js文件以包含以下内容:

require('es6-promise').polyfill()
import Vue from 'vue/dist/vue.common.js'
import Vuex from 'vuex/dist/vuex.js'
import Items from '../../../src/components/Items.vue'
import * as types from '../../../src/store/mutation-types'
import * as getters from '../../../src/store/getters'

describe('Items.vue', () => {
  it('test initial rendering with mock data', (done) => {
    const actionsInjector = require('inject-loader!../../../src/store/actions')
    const actions = actionsInjector({
      './api': {
        getItems () {
          return new Promise((resolve, reject) => {
            const arr = ['Cat', 'Dog', 'Fish', 'Snail']
            resolve(arr)
          })
        }
      }
    })

    const state = {
      items: []
    }

    const mutations = {
      [types.setItems] (state, items) {
        state.items = items
      }
    }

    const options = {
      state,
      mutations,
      actions,
      getters
    }

    const mockStore = new Vuex.Store(options)

    const vm = new Vue({
      template: '<div><test></test></div>',
      store: mockStore,
      components: {
        'test': Items
      }
    }).$mount()

    Vue.nextTick()
      .then(() => {
        expect(vm.$el.querySelectorAll('.items').length).to.equal(4)
        done()
      })
      .catch(done)
  })
})

在actions.js中奇怪的内联需求基本上注入了我们真实的actions.js文件,但允许我们存储该文件的依赖关系。 该文件中的下一行演示了如何使用actionsInjector完成。 我们用我们自己的API来替换我们的API对象,并在测试中返回一致的数据 就是这样,现在我们所要做的就是运行:

$ npm test run

开始享受吧,全都是绿色!!!🎉🍕

共收到 0 条回复
1楼 已删除
es6china 将本帖设为了精华贴 04月16日 15:34
es6china 取消了精华贴 04月16日 15:34
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册