このガイドでは、vue-test-utils
でコンポーネントで Vuex をテストする方法について、見ていきます。
それではいくつかのコードを見ていきましょう。
これはテストしたいコンポーネントです。これは Vuex のアクションを呼び出します。
<template>
<div class="text-align-center">
<input type="text" @input="actionInputIfTrue" />
<button @click="actionClick()">Click</button>
</div>
</template>
<script>
import { mapActions } from 'vuex'
export default{
methods: {
...mapActions([
'actionClick'
]),
actionInputIfTrue: function actionInputIfTrue (event) {
const inputValue = event.target.value
if (inputValue === 'input') {
this.$store.dispatch('actionInput', { inputValue })
}
}
}
}
</script>
このテストの目的のために、アクションが何をしているのか、またはストアがどのように見えるかは気にしません。これらのアクションが必要なときに発行されていること、そして期待された値によって発行されていることを知ることが必要です。
これをテストするためには、私たちのコンポーネントを shallow するときに Vue にモックストアを渡す必要があります。
ストアを Vue コンストラクタベースに渡す代わりに、localVue に渡すことができます。localeVue はグローバルな Vue コンストラクタに影響を与えずに、変更を加えることができるスコープ付き Vue コンストラクタです。
これがどのように見えるか見ていきましょう:
import { shallow, createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
import Actions from '../../../src/components/Actions'
const localVue = createLocalVue()
localVue.use(Vuex)
describe('Actions.vue', () => {
let actions
let store
beforeEach(() => {
actions = {
actionClick: jest.fn(),
actionInput: jest.fn()
}
store = new Vuex.Store({
state: {},
actions
})
})
it('calls store action actionInput when input value is input and an input event is fired', () => {
const wrapper = shallow(Actions, { store, localVue })
const input = wrapper.find('input')
input.element.value = 'input'
input.trigger('input')
expect(actions.actionInput).toHaveBeenCalled()
})
it('does not call store action actionInput when input value is not input and an input event is fired', () => {
const wrapper = shallow(Actions, { store, localVue })
const input = wrapper.find('input')
input.element.value = 'not input'
input.trigger('input')
expect(actions.actionInput).not.toHaveBeenCalled()
})
it('calls store action actionClick when button is clicked', () => {
const wrapper = shallow(Actions, { store, localVue })
wrapper.find('button').trigger('click')
expect(actions.actionClick).toHaveBeenCalled()
})
})
ここでは何が起こっているでしょうか?まず、Vue に localVue.use
メソッドを使用して Vuex を使用するように指示しています。これは、単なる Vue.use
のラッパです。
次に、新しい Vuex.store
をモックした値で呼び出すことによってモックのストアを作成します。それをアクションに渡すだけです。それが気にしなければならないことの全てだからです。
アクションは、Jest のモック関数です。これらモック関数は、アクションが呼び出された、または呼び出されていない、かどうかを検証するメソッドを提供します。
アクションのスタブが期待どおりに呼び出されたことを検討することができます。
今、ストアを定義する方法が、あなたには少し異質に見えるかもしれません。
各テストより前にストアをクリーンに保証するために、beforeEach
を使用しています。beforeEach
は各テストより前に呼び出される Mocha のフックです。このテストでは、ストア変数に値を再度割り当てています。これをしていない場合は、モック関数は自動的にリセットされる必要があります。また、テストにおいて状態を変更することもできますが、この方法は、後のテストで影響を与えることはないです。
このテストで最も重要なことは、モック Vuex ストアを作成し、それを vue-test-utils に渡す ことです。
素晴らしい!今、アクションをモック化できるので、ゲッタのモックについて見ていきましょう。
<template>
<div>
<p v-if="inputValue">{{inputValue}}</p>
<p v-if="clicks">{{clicks}}</p>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default{
computed: mapGetters([
'clicks',
'inputValue'
])
}
</script>
これは、かなり単純なコンポーネントです。ゲッタによる clicks
の結果と inputValue
を描画します。また、これらゲッタが返す値については実際に気にしません。それらの結果が正しく描画されているかだけです。
テストを見てみましょう:
import { shallow, createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
import Actions from '../../../src/components/Getters'
const localVue = createLocalVue()
localVue.use(Vuex)
describe('Getters.vue', () => {
let getters
let store
beforeEach(() => {
getters = {
clicks: () => 2,
inputValue: () => 'input'
}
store = new Vuex.Store({
getters
})
})
it('Renders state.inputValue in first p tag', () => {
const wrapper = shallow(Actions, { store, localVue })
const p = wrapper.find('p')
expect(p.text()).toBe(getters.inputValue())
})
it('Renders state.clicks in second p tag', () => {
const wrapper = shallow(Actions, { store, localVue })
const p = wrapper.findAll('p').at(1)
expect(p.text()).toBe(getters.clicks().toString())
})
})
このテストはアクションのテストに似ています。各テストの前にモックストアを作成し、shallow
を呼び出すときにオプションを渡し、そしてモックゲッタから返された値を描画されているのを検証します。
これは素晴らしいですが、もしゲッタが状態の正しい部分を返しているのを確認したい場合はどうしますか?
モジュールはストアを管理しやすい塊に分けるために便利です。それらはゲッタもエクスポートします。テストではこれらを使用することができます。
コンポーネントを見てみましょう:
<template>
<div>
<button @click="moduleActionClick()">Click</button>
<p>{{moduleClicks}}</p>
</div>
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
export default{
methods: {
...mapActions([
'moduleActionClick'
])
},
computed: mapGetters([
'moduleClicks'
])
}
</script>
1 つのアクションと 1 つのゲッタを含む単純なコンポーネントです。
そしてテストは以下のようになります:
import { shallow, createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
import Modules from '../../../src/components/Modules'
import module from '../../../src/store/module'
const localVue = createLocalVue()
localVue.use(Vuex)
describe('Modules.vue', () => {
let actions
let state
let store
beforeEach(() => {
state = {
module: {
clicks: 2
}
}
actions = {
moduleActionClick: jest.fn()
}
store = new Vuex.Store({
state,
actions,
getters: module.getters
})
})
it('calls store action moduleActionClick when button is clicked', () => {
const wrapper = shallow(Modules, { store, localVue })
const button = wrapper.find('button')
button.trigger('click')
expect(actions.moduleActionClick).toHaveBeenCalled()
})
it('Renders state.inputValue in first p tag', () => {
const wrapper = shallow(Modules, { store, localVue })
const p = wrapper.find('p')
expect(p.text()).toBe(state.module.clicks.toString())
})
})