こんにちは!
株式会社SCOUTER開発部フロントエンドエンジニアの佐藤(@r_sato1201)です
最近、社内でLaravelのテストについての会話が活発に交わされるようになりました。 その会話を聞いていて、Vueのテストはないのだろうか?と興味を持ったのでテストの導入方法と、簡単なテストを動かしてみたことについて書いてみました。
なぜテストをするのか?
Vueの公式ドキュメントにはテストの利点についてこう記述してあります。
コンポーネントの単体テストにはたくさんの利点があります:
・コンポーネントがどう動作すべきかのドキュメントを提供します
・過度な手動テストの時間を節約します
・新しい機能におけるバグを減らします
・設計を改良します
・リファクタリングを容易にします
自動テストは大規模な開発チームが複雑なコードベースを保つことを可能にします。
テストを書くことで、コンポーネントがどういう振る舞いをするべきかや、 コンポーネントの設計に問題があることに気づけるメリットがあるようです。
テストの導入方法
Vueにはvue-test-utils
というVueコンポーネントの単体テストのための公式ライブラリが用意されています。上記に加え、テストフレームワークのJest
を使うことで簡単にテストを導入することが出来ます。
新しくプロジェクトを作成する際は
vue-cli
のvue create
コマンドを用いればvue-test-utils
、Jest
を簡単にインストールすることができます。
Unit Testingを選択
Jestを選択
既存のプロジェクトに導入する際は、devDependencies にvue-test-utils、Jestをインストールしましょう。
yarn add -D @vue/test-utils babel-jest jest vue-jest
※babelを使っていない場合、babel-jestは不要です。
Jest
はカバレッジも簡単に取ることが出来ます。
package.json
にcollectCoverage
オプションを加えることでカバレッジを取ることができるようになり、collectCoverageFrom
オプションにカバレッジ収集対象のファイルを配列で定義することができます。
"collectCoverage": true, "collectCoverageFrom": ["**/*.{js,vue}", "!**/node_modules/**"],
更に詳しいオプションの詳細については、 Jest configuration documentationも参考にしてみてください。
テストを実践してみよう
テストの書き方を掴むために、propsによる描画のテストを行ってみます。
以下のようなpropsを持つ簡単なコンポーネントを作成します。
RemoveUser.vue
<template> <div class="User"> <span>{{name}}</span> <template v-if="isAdmin"> <button @click="removeUser()">削除</button> </template> </div> </template> <script> export default { props: { id: Number, name: String, isAdmin: Boolean }, methods: { removeUser() { this.$emit('removeUser', {id: this.id}) } } } </script>
それでは、実際にテストを行ってみましょう。
プロジェクトのtests/unit
内にテストファイルを作成します。
テストは.spec.js
や.test.js
というファイルを作成すれば自動で検知してくれます。
yarn test:unit
でテストを実行します。
また、--watch
を指定することで、テストコードに変更を検知して、変更があるたびにテスト実行することができます。
RemoveUser.spec.js
import {mount} from '@vue/test-utils' import RemoveUser from '@/components/RemoveUser.vue' describe("user", () => { const adminPropsData = { id: 1, name: 'ユーザー1', isAdmin: true, } const userPropsData = { id: 1, name: 'ユーザー1', isAdmin: false, } test('emitされるか', () => { const wrapper = mount(RemoveUser, { propsData: adminPropsData }) wrapper.find('button').trigger('click') // イベント発火してるかどうか expect(wrapper.emitted('removeUser')).toBeTruthy() // emit時にidが渡されてくるかどうか expect(wrapper.emitted('removeUser')[0]).toEqual([{id: 1}]) }) test('nameが正しく表示されているか', () => { const wrapper = mount(RemoveUser, { propsData: adminPropsData }) expect(wrapper.find('span').text()).toBe(adminPropsData.name) }) test('isAdminの値によって削除ボタンが表示されてるか', () => { const adminWrapper = mount(RemoveUser, { propsData: adminPropsData }) const userWrapper = mount(RemoveUser, { propsData: userPropsData }) expect(adminWrapper.find('button').exists()).toBeTruthy() expect(userWrapper.find('button').exists()).toBeFalsy() }) })
テストコードを箇所に分けて、説明していきます。
test('emitされるか', () => { const wrapper = mount(RemoveUser, { propsData: adminPropsData }) wrapper.find('button').trigger('click') // イベント発火してるかどうか expect(wrapper.emitted('removeUser')).toBeTruthy() // emit時にidが渡されてくるかどうか expect(wrapper.emitted('removeUser')[0]).toEqual([{id: 1}]) })
ここでは、コンポーネント内でクリックイベントが発火したときに 正しくemitされているかを確認しています。
ここで
・ボタンをクリックしたときにemitが動いているか
・emit時に、propsで渡したidが返ってくるか
が分かります。
test('nameが正しく表示されているか', () => { const wrapper = mount(RemoveUser, { propsData: adminPropsData }) expect(wrapper.find('span').text()).toBe(adminPropsData.name) })
ここでは、propsで渡したnameが正しく描画されているかを確認しています。
test('isAdminの値によって削除ボタンが表示されてるか', () => { const adminWrapper = mount(RemoveUser, { propsData: adminPropsData }) const userWrapper = mount(RemoveUser, { propsData: userPropsData }) expect(adminWrapper.find('button').exists()).toBeTruthy() expect(userWrapper.find('button').exists()).toBeFalsy() })
ここでは、権限によって削除ボタンが表示の出し分けをしているかを確認しています。
ここで
・isAdmin = true(管理者) の場合、削除ボタンが表示されている
・isAdmin = false(一般) の場合、削除ボタンが表示されていない
ということが分かります。
ちなみに、開発をする上では
① 実装する機能の要件を元にテストコードを書く
② テストコードのに表現されている要件を満たすコードを書く
③ テストが成功する状態を維持しつつリファクタリングしていく
という順番で行うと良いと思います。
さいごに
フロントのテストはどんなものなんだろうと、軽い気持ちでテストについて調べてみました。 導入自体は簡単で手軽に始められますが、何をテストするべきか、しないべきかの切り分けが非常に難しく重要であると感じました。 今回は、Vueコンポーネントの単体テストの導入方法、シンプルなテストに留めましたが、 今後、Vuex周りのテストやE2Eテストについても調べ学習していきたいと思います。
現在、株式会社SCOUTERでは、エンジニア、デザイナーの募集をしております。
興味のある方は、是非下記からご応募お願い致します!
参考資料
Vue Test Utils
https://vue-test-utils.vuejs.org/ja/Vue.jsクックブック
https://jp.vuejs.org/v2/cookbook/unit-testing-vue-components.htmlVue.jsテストハンドブック
https://lmiller1990.github.io/vue-testing-handbook/ja/Jest configuration documentation
https://jestjs.io/docs/en/configuration.html#collectcoverage-boolean