Edited at

Vue.js+Vuex環境のJestを用いたテストの書き方 (Store編:破急)


:beginner: はじめに

本記事ではQiita覇権でおなじみVue.js+Vuex環境のテストを紹介します:raising_hand:

Store=Vuex周りのテストの書き方となります。

下記の記事の続きとなっておりますので、見てない方はそちらも。

Vue.js+Vuex環境のJestを用いたテストのやり方 (Component編:序)


:pencil: テスト対象

引き続きVuex公式の実装例であるTODO機能に対してテストコードを記述します。

https://github.com/vuejs/vuex/tree/dev/examples/todomvc

上記の store に対してテストを行います。


:pencil: テストコード


test/store/index.spec.js

import { createLocalVue } from '@vue/test-utils'

import Vuex from 'vuex'
import { cloneDeep } from 'lodash'
import actions from '~/store/actions.js'
import { mutations } from '~/store/mutations.js'
import plugins from '~/store/plugins.js'

const state = {
todos: []
}

// beforeEachで毎回Storeを生成するために。
const initStore = () => {
return cloneDeep({
state,
mutations,
actions,
plugins
})
}

describe('store', () => {
let store
let localVue

// 実行可能なStoreを生成してテストします。
beforeEach(() => {
localVue = createLocalVue()
localVue.use(Vuex)
// ここも通常の使い方ではほぼmoduleモードだと思います。
store = new Vuex.Store(
initStore()
)
})

// Actionsの関数をテストするというより、Componentに対するdispatchを通じたAPIをテストする意識
it('dispatch addTodo', () => {
// 実際にはActionsを通してMutationsからStateを変更しているが、
// ComponentのインタフェースであるActionsをdispatchして前後のstateを検証する。
expect(store.state.todos).toEqual([])
store.dispatch('addTodo', 'foo')
expect(store.state.todos).toEqual([{
text: 'foo',
done: false
}])
})

// 複雑なケースでもそこに至るまでの状態を用意してから、dispatch前後のstateを検証する。
it('dispatch clearCompleted', () => {
store.dispatch('addTodo', 'foo')
store.dispatch('addTodo', 'bar')
store.dispatch('toggleTodo', {
text: 'bar',
done: false
})

expect(store.state.todos).toEqual([
{
text: 'foo',
done: false
},
{
text: 'bar',
done: true
}
])

// 今回テストしたいActions
store.dispatch('clearCompleted')

expect(store.state.todos).toEqual([{
text: 'foo',
done: false
}])
})
})


※元気が出てきた次第にサンプルコード加筆します…


:japanese_goblin: Storeテストのまとめ

・ComponentのAPIに相当するActionsの機能テストをしていくイメージ。

 ※Componentから直接Commitしないルール付けはとても有用です。

・実行可能なストアを生成してテストを行う。

 ※実行可能なストアについて

そもそも出来る限りテストしなくて済むために、以下を心がけましょう。

・acitionsのpayloadは検証する。(※TypeScriptで型を付けるなど)

・stateはStoreにとってのエンティティ。InterfaceやGenericsを定義しましょう。

 ※stateオブジェクトの中身が定義されてないと、必ず辛く悲しい道を辿る😭

アプリケーションにとって重要でないstateがStoreに定義されていないこと

 ※どういった情報をvuexの単方向で処理し、どこまでvueの簡易的な双方向で済ませるかの切り分けがvuexではとても大切です。

 この設計に自信がなければ、結果的にReduxでFluxを強制した方が良かったとなりかねません!


:japanese_goblin::japanese_goblin: まとめのまとめ

ここまで書いといて何ですが、テストはどう書くかより何を書くか(何を担保したいか)を明確にすることが大切だと思います。

でないとStringはStringであるみたいなトートロジーで、カバレッジ稼ぐ悲しい物語が生まれます😭

個人的にはテストのしやすさからComponentとStoreは分けてテストし、APIを叩く場合はモックしますが、

JestのテストにおいてもComponentとStoreをつなげてテストしたり、APIをモックしないなど、

より大規模なTestSizeで担保していくことも有用だと思います。

いずれにしろフロントエンドでは、テスト手法やそれら組み合わせのベストプラクティスを模索中だと感じています。

個人的には、FacebookやGoogleが現実のDOMやブラウザの差異を吸収し、Jestのようなnode環境で高速に動作するテストランナーだけで、E2Eテストが要らなくなる未来を期待してますが🙏


皆さんのテストは、なんですか?