Nuxt で Vuex (ストアモジュール分割) に依存したコンポーネントをテストする

2021/05/03に公開

はじめに

Nuxtでストアをモジュール分割したVuexをテストする際、コンポーネント側でコールしたmapGetters('counter', ['count'])などの名前空間を認識してくれない現象が発生します。

NuxtはVuexのモジュールを自動でインポートしてVuexインスタンスを生成してくれますが、テスト時はこの自動インポートが行われないので名前空間を認識できない様です。

以下に対策を記しますが、他にも良い方法があればコメントいただければ幸いです。

前提条件

この記事はVuexに依存しているページコンポーネントをテストします。

また、テストはjestに加えてVue.js向けのテストライブラリであるvue-test-utilsがインストールされている前提で記述しています。

https://github.com/vuejs/vue-test-utils

テスト対象

Vuex store

まずはVuexモジュールの例です。
前述の通りNuxtは以下のコードだけで自動で名前空間付きのモジュールとして解釈してくれます。

store/counter.js
const state = () => ({
  count: 0
})

const getters = {
  count: state => state.count
}

const mutations = {
  increment(state) {
    state.count++
  }
}

const actions = {
  increment({ commit }) {
    commit('increment')
  }
}

export default {
  state,
  getters,
  mutations,
  actions
}

Pages

次にページコンポーネントです。Vuexから値とアクションを取得して表示しているだけの簡単なものです。今回はこのコンポーネントをテストします。

pages/counter.vue
<template>
  <div>
    <p>Count: {{ count }}</p>
    <button type="button" @click="increment">Increment</button>
  </div>
</template>

<script>
import { mapGetters, mapActions } from 'vuex'

export default {
  computed: {
    ...mapGetters('counter', ['count'])
  },

  methods: {
    ...mapActions('counter', ['increment'])
  }
}
</script>

テストコード

単純にstore/counter.jsをimportしてVuexインスタンスを生成しても、counterモジュールが名前空間として登録されていないので以下の様にエラーがでます。

 [vuex] module namespace not found in mapGetters(): counter/

モジュールオブジェクト(この例ではコピーしたオブジェクト)にnamespacedプロパティを追加します。これによってNuxtに頼らずにVuexにcounter名前空間を登録することでき、ページコンポーネントのmapGetters('counter', ['count'])がテストでも動作するようになります。

test/pages/counter.test.js
import Vuex from 'vuex'
import { shallowMount, createLocalVue } from '@vue/test-utils'
import cloneDeep from 'lodash.clonedeep'
import counterPage from '~/pages/counter'
import counterStore from '~/store/counter'

const localVue = createLocalVue()
localVue.use(Vuex)

describe('pages/counter.vue', () => {
  let store

  beforeEach(() => {
    const clone = cloneDeep(counterStore)

    // ※モジュールを名前空間として登録する為の設定
    clone.namespaced = true

    // ※モックのVuexインスタンスを生成
    store = new Vuex.Store({
      modules: {
        counter: clone
      }
    })
  })

  it('ボタンのクリックでカウントの値が「+1」される', () => {
    const wrapper = shallowMount(counterPage, { store, localVue })
    expect(wrapper.vm.count).toBe(0)
    wrapper.find('button').trigger('click')
    expect(wrapper.vm.count).toBe(1)
  })
})

参考

https://vuex.vuejs.org/ja/guide/modules.html
https://vue-test-utils.vuejs.org/ja/guides/#コンポーネント内の-vuex-をテストする

Discussion