みなさん、フロントエンドのテスト書いていますか?
Nuxt.js(Vue.js)でJestを使ってコンポーネントのテストについては情報がいろいろありましたが、VuexのStoreのテストについてあまり情報がなかったので、今回はVuexStoreのテストについてまとめました。
「Nuxt v2とFirebase(CloudFirestore)でPWA対応Webアプリ開発」で作成したアプリケーションをJestでテストする手順についてまとめていきます。
テストの準備
テストに必要なツール
Jest
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
"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にソースも公開されています。
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でテストを最初に書くことが大事ですね。