Edited at

JestでNuxt.js(Vue.js)のVuexをテスト

みなさん、フロントエンドのテスト書いていますか?

Nuxt.js(Vue.js)でJestを使ってコンポーネントのテストについては情報がいろいろありましたが、VuexのStoreのテストについてあまり情報がなかったので、今回はVuexStoreのテストについてまとめました。

Nuxt v2とFirebase(CloudFirestore)でPWA対応Webアプリ開発」で作成したアプリケーションをJestでテストする手順についてまとめていきます。


テストの準備


テストに必要なツール


Jest

jest.png

https://github.com/facebook/jest

JestはFacebookが開発を進めるフロントエンドのテストランナです。

Facebook製なので当然Reactに対応していますが、その他JavaScriptアプリケーションのテストにも使用でき、Vue.jsでも利用できます。

RSpecライクな記法でテストコーディングが可能です。


Vue Test Utils

https://vue-test-utils.vuejs.org/ja/

Vue.jsアプリケーション用の公式単体テストライブラリです。

Vue-Router、VuexなどVue.jsのテストコーディングのために必要です。


Babel (@babel/core)

BabelはES2015以降のバージョンで記載されたJavaScriptをECMAScript5に変換するためのトランスコンパイラです。


babel-jest

JestをBabelに対応するためのツールです。


babel-preset-env

環境に従って必要なBabelプラグインを自動で決定するツールです。


babel-preset-vue-app

Vue開発のためのBabel設定を提供するツールです。


lodash.clonedeep

テスト実行のために直接必要になるわけではありませんが、VuexのテストでStoreオブジェクトをコピーするために利用します。


必要なツールを設定

必要なツールをインストールします。

> yarn add jest @vue/test-utils lodash.clonedeep babel-jest @babel/core @babel/preset-env babel-preset-vue-app lodash.clonedeep --dev

package.jsonにtestコマンド、Jest、Babelの設定を追加します。

targets->browsersオプションで対象ブラウザを指定しています。

オプションの詳細はこの辺りに記載があります。

https://babeljs.io/docs/en/babel-preset-env


/package.json

  "scripts": {

"test": "jest"
},
"jest": {
"transform": {
"^.+\\.js$": "babel-jest"
}
},
"babel": {
"presets": [
[
"@babel/preset-env",
{
"targets": {
"browsers": [
"last 2 versions"
]
},
"debug": true
}
]
]
},

この状態でテストを実行してみます。

> yarn run test

yarn run v1.10.1
$ jest
No tests found

まだテストコードがないので、エラーになりますが、テストが実行されています。


テストコーディング


Vuexのテスト

VuexのStoreのテストはこちらの書籍を参考にしました。



Nuxt.jsビギナーズガイド

GitHubにソースも公開されています。


/spec/store/messages.spec.js

require('dotenv').config()

const Vuex = require('vuex')
const messages = require('../../store/messages')
const { createLocalVue } = require('@vue/test-utils')
const cloneDeep = require('lodash.clonedeep')

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

describe('store/message.js', () => {
let store

beforeEach(() => {
store = new Vuex.Store(cloneDeep(messages))
})

describe('actions', () => {
test('initMessagesでmessagesが初期化される', async () => {
expect(store.getters['messages'].length).toBe(0)
await store.dispatch('initMessages')
expect(store.getters['messages'].length).not.toBe(0)
})
})
})


少し説明すると、require('dotenv').config()で、.envの値を読み込みます。

beforeEachで、cloneDeepを使ってmessagesのStoreをコピーし、オブジェクトを生成します。

  beforeEach(() => {

store = new Vuex.Store(cloneDeep(messages))
})

実際のテストはこちらです。

StoreのinitMessagesアクションをdispatchして、messagesステートにデータがセットされることを確認しています。

  describe('actions', () => {

test('initMessagesでmessagesが初期化される', async () => {
expect(store.getters['messages'].length).toBe(0)
await store.dispatch('initMessages')
expect(store.getters['messages'].length).not.toBe(0)
})
})

テストを実行します。

 PASS  spec/store/messages.spec.js

store/message.js
actions
✓ initMessagesでmessagesが初期化される (830ms)

Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 3.223s
Ran all test suites.

テストが成功しました!


まとめなど

テスト環境構築の際、Babelのエラーで苦しみました。。

this.setDynamic is not a function

とか、

Cannot find module '@babel/preset-env' 

とか。

Babelのバージョンに気をつけつつ、GitHubのissue情報や、Babel公式ドキュメントあたりにいろいろ情報ありますので、参考にしてください!

今回の記事では書かなかったですが、コンポーネント周りのテストをやろうとすると、やはりコンポーネントがテストしやすいIOの単位でまとまっていないと辛いです・・テストどう書けばいいの?ってなります。

やはり、TDDでテストを最初に書くことが大事ですね。