虎の穴開発室ブログ

虎の穴ラボ株式会社所属のエンジニアが書く技術ブログです

MENU

Vue.js初心者でも書ける!ゼロから始めるVuexのテスト入門編その1(vue-test-utils)

みなさん、いかがお過ごしでしょうか。今年の7月からラボのマーケチームに加わったY.I.です。
好きな作品は「ゆるキャン△」です。お盆休みには「ゆるキャン△」舞台の富士山周辺でキャンプツーリングをしてきました。
ゆるキャン△に関しては熟知している私ですが、技術的にまだまだ未熟な開発者です・・・。活躍できるよう頑張っていきますので、どうぞよろしくお願いします。

さて、今回はVue.jsのテスト、とくにVuexのテストについて書いていきたいと思います。

0. この記事を読んでわかること

本連載では「ゼロから始めるVuexのテスト入門編」と題して、これまでVuexのテストを書いたことがない方を対象に、全2回に渡ってVuexのテストの詳細を書いていきます。 今回は連載の第一回目として、下記の構成でお送りします。テストの必要性やポイント、またVuexのテストがどのようなものになるのか感じ取っていただけたら幸いです。

  1. フロント開発にテストは必要?
  2. Vuexをテストする上でのポイント
  3. Vuexをテストする

※ テストコードを先に見たい方は、3. Vuexをテストするをご覧ください。

また、第2回目では「Vuexを使用したコンポーネントのテスト方法」を中心に、vue-test-utilsの機能をはじめとした、更に詳細な内容に触れていく予定です。こちらも楽しみにいただけると幸いです。

それでは次章より本編スタートです・・・!

1. フロント開発にテストは必要?

突然ですが、そもそもフロント開発にテストは必要でしょうか。 これには様々な意見があるかと思います。

ここでは、これまで私が耳にしたことのある「テスト不要論」を例にとり、その反論を上げる形で必要性を考えてみたいと思います。

1.1. テストに対する様々な意見

意見1: 実装を見れば何が起きているかわかるから不要

この意見、すごくわかります。私も同じように考えがちです(というか少し前の私です)。。。

丁寧な設計を行い、きれいにコードを書けばバグは発生しないので、テストを書くことよりも丁寧な設計・実装に注力するすべきだという意見です。

しかし、次のような状況で綺麗な実装が維持できるでしょうか。

  • 大規模な開発
  • 複数人での開発
  • 度重なる仕様変更
  • チームメンバーの入れ替わり などなど...

こういった状況においては、当初の設計やコードの綺麗さを保つのは難しいです。

これらの状況はバックエンド開発だけでなく、フロントエンド開発でももちろん発生します。 またWeb技術の進歩に伴い、フロント開発の複雑さや多様さは年々増しています。 チームでの開発や長期にわたる開発を想定した場合は、バックエンドだけでなくフロントエンドにおいてもあった方がよさそうです。

意見2: プリントデバッグや実機テストでもよくない?

すごくわかります。私も同じように考えがちです(というか少し前の私です)。。。

しかしテストの中には、プリントデバッグや実機テストだけでは対処し辛い実装もあります。 例としてボタンをクリックした時の挙動のテストを考えてみます。

プリントデバッグを使ってボタンの挙動を確認する場合は、開発者が実際にブラウザ上でボタンをクリックし、その結果を目視で確認する必要があります。この作業は一度や二度であれば良いかもしれません。しかし実際の開発では、度重なる変更によりデバッグを何度も行うことは少なくありません。仕様が変更した時や、他のメンバーが実装に手を加えたときなど、その度に動作確認をするのは労力の面や、人為的なミスの危険性を考えて避けるべきです。

しかし、Jestのようなテストツールを使用することで、ユーザーの動作についても(一部は)コード化することができます。

例として、Jestを使用したボタン押下時のテストコードを見てみましょう。以下の例では、「はい」を押下時にアクションが実行されることを検証しています。

describe('クリックアクションの検証', () => {
    it('「はい」を押下時にアクションが実行されること', () => {
      const wrapper = mount(ToastGameEnd, { store, localVue })
      ...
      button.trigger('click')
      ...
      expect(actions.resetGame).toHaveBeenCalled()
    }),
...

jestjs.io

上記のようにテストコードを用意することで、ユーザーの動作をコードがすることができます。これにより人為的ミスの発生を防ぐことができます。 また、一度テストコードを用意してしまえば何度でも使いまわせるため、デバッグに伴う労力が削減されます。

さらに、多くのテストフレームワークでは求めるテスト結果を正確に書くことができるので(上記の例では「はい」を押下時にアクションが実行されることとしている)、テスト観点を明確にコード化することができます。

以上のように、プリントデバッグや実機テストに比較して、大きなメリットがあります。

  • ヒューマンエラーを防ぐことができる。
  • 使い回しが効くため省力化になる。
  • 求めるテスト結果を正確に書くことができるので、観点が明確になる。

もちろん、簡易的なチェックのためのプリントデバッグは支障ないと思いますが、クリティカルな実装箇所については可能な限りコード化すべきです。

意見3: それでも気が進まないなあ

すごくわかります。私も同じように考えがちです(というか少し前の私です)。。。

特に呼び出すAPIが未完成であったり、仕様変更の可能性が大きかったりする場合は、書いたテストが無駄になってしまう可能性があります。 こういった状況ではテストを書くべきでしょうか。 これはケースバイケースかもしれません。

しかしそういった時でも、テストを書くことで得られるメリットはあります。

例として、以下のようなメリットが挙げられます。

  • 実装の不安感をコントロールできる。
  • 視野が広がりテストしやすいコード(綺麗なコード)が書けるようになる。
  • テストを書いている上で、設計の誤りに気付ける。

特に自分がメリットとして強く感じていることは、「テストを書いている上で、設計の誤りに気付ける」です。

この点に関して、書籍「テスト駆動開発」1 でおなじみの@t_wadaさんが、以下のような意見をおっしゃっていました。

TDDの考え方でテストを書いていくと、結果として不具合も減りますし、なにより設計そのものの良し悪しに気づけるようになります。テストが書きにくいプログラムというは、そもそも設計に問題があることが多いんです。
引用: テストコードを書く文化を根付かせたい─和田卓人

今回Vuexのテストを書いている際、ひつとのVueコンポーネントが大きくなっていることが原因で、テストが書きにくく感じる場面がありました。こういった設計や実装の誤りに気づくことができるのも、テストを書くことのメリットだと言えます。

2. Vuexをテストする上でのポイント

ここまで読んでくださった方は、もしかすると「テストを書くことも悪くないかな・・」と思ってくださったかもしれません。 ここからは、実際にVuexのテストを見ていきたいと思います。

まずは、Vuexのテストする上でのポイントを見ていきます。

2.1. 状態をテストする必要がある

一つ目は「状態をテストする必要がある」です。Vuexは状態管理ライブラリなので、その状態をテストするのは当然といえば当然ですが。

しかし、現在デファクトとなっているテストツールの Jestmocha, chai 自体には、Vuexをテストする機能がありません。そのため、これらのテストツールだけではVuexの保持する状態をテストすることができません。

そこで、これらを可能にするために、Vue公式で推奨されている vue-test-utils を使用します。

vue-test-utils.vuejs.org

このライブラリを使用することで、Vuexのstoreを(モックあるいはインポートを通して)用意できるため、テストをすることが可能となります2

そしてこのライブラリは、Vue-CLIでプロジェクトを作成した場合はデフォルトで組み込むことが可能なので、特別インストールや設定に手間がかかることもありません。

ただ、このvue-test-util使用してもなお、注意するべきポイントがあります。それは、副作用をどう扱うかということです。

2.2. 副作用を考える必要がある

SPAをはじめとするモダンなフロントエンド開発では、様々な副作用(ここでは外部ライブラリの呼び出しやサーバーサイドとの通信による状態の変更を想定)が発生します。これにより、Vuexが保持する状態のテストは量的、質的に困難なものとなります。

こういった副作用を含む機能は、基本的にはVuexのactionで定義することを推奨されています。つまり我々が考えるべきことは、その副作用を含むactionをどのようにテストするか、ということです。

これに対しては、いくつかのアプローチがあります。

  1. Jestのモック機能を使用し、actionの結果を確認する3
  2. Jestのモック機能は使用するが、actionが実行されたことのみをテストする4

1の方法では、Jestが提供しているモック機能を使用することで、外部ライブラリやサーバーサイドから返される結果をモックするものです。この方法が取れるのあれば、それがベターかもしれません。今回の私の実装に限って言えば、手探りで開発を進めていた部分もあり、ひとまず2の方針のみの対応としました。複雑なactionsを含んでいない場合は、Vuexのmutationsやgettersのテストで間に合うこともあるかもしれません。

というわけで、副作用を含む箇所については、Jestのモックを使用することで対応可能であることがわかりました。

3. Vuexをテストする

ここまでは、テストを書くことの必要性について、またVuexをテストする際に検討するポイントについて書いてきました。 それでは実際に、Vuexのテストコードを見ていきたいと思います。

3.1. gettersのテスト

gettersのテストについて、まずは簡単な例から見ていきたいと思います。 gettersを定義しているgetters.jsと、そのテストを定義しているgetters.spec.jsについて記します。

// getters.js
export default {
  // getter関数を用意
  playerChara: state => 'chara' + state.player.chara,
}
// getters.spec.js
import getters from '../../../src/store/getters'

defaultState = { chara: '001' }

describe('gettersの検証', () => {
  it('playerChara', () => {
    // Jestのexpect関数を使用してgetter関数をテスト
    expect(getters.playerChara(defaultState)).toBe('chara001')
  })
})

シンプルにgetter。単純にgetterの動きをテストするだけであれば、ダミーのstate(状態)を作成して、それに対するgetterの結果をアサートするのみで完了です。

ですが、このようなシンプルなgetter関数だけではなく、中には複雑な関数も存在します。とはいえ、そちらを検証する際も、基本の考え方は同じです。

続いてはmutationsのテストです。

3.2. mutationsのテスト

mutationsのテストも、こちらも考え方はgettersのテストと同じです。ダミーのstateに対してmutationを実行し、その結果を確認します。

注意点としては、mutationではstateの変更が発生する点です。同一のstateを複数のテストで使い回すと、前のテストの状態(state)を引きずってしまう危険性があります。そこで他のテストへの影響を防ぐため、beforeEach関数やObject.assignを使用して、新しいstateをテストごとに用意します。

ここではmutationsを定義しているmutations.jsと、そのテストを定義しているmutations.spec.jsについて記します。

// mutations.js
export default {
  // mutation関数を用意
  updatePlayer(state, payload) {
    state.player = payload
  },
}
// mutations.spec.js
const defaultState = {
  player: {}
}
describe('mutationの検証', () => {
  it('updatePlayer', () => {
    // 新しいstateを用意
    const state = Object.assign({}, defaultState)
    const dummyPayload = {
      id: 100,
      name: '虎ラボ太郎',
      visible: true,
      chara: '011',
    }

    // mutationを実行
    mutations.updatePlayer(state, dummyPayload)

    // Jestのexpect関数を使用してstateの状態を検証
    expect(state.player).toEqual(dummyPayload)
  })
}

このように、Vuexに定義したgetters, mutationsのテストは、ダミーのstateを用意することで検証可能であることがわかりました。

4. おわりに

今回は、以下のような構成でお送りしました。

  1. フロント開発にテストは必要?
  2. Vuexをテストする上でのポイント
  3. Vuexをテストする

次回は、「ゼロから始めるVuexのテスト入門編その2」と題して、Vuexを使う側のコンポーネントに対するテストの解説を行います。

vue-test-utils の優れた機能(mount/shallowMountcreateLocalVue)を使用し、commitactionを含む複雑なコンポーネントのテストを書いていく想定ですので、こちらも楽しみにいただけると幸いです。

P.S.

オンライン座談会イベント

「虎の穴ラボ」社員による3回目のオンライン座談会イベントが 9/18(金)19:30〜 開催します!

虎の穴ラボのエンジニア達が【最新技術】や【働きかた改革】、ちょっと脱線して【アニメ】の話題など、オタク×ITな目線と感性でお話しします。 yumenosora.connpass.com

カジュアル面談

弊社エンジニアと1on1で話せます、カジュアル面談も現在受付中です!こちらも是非ご検討ください。 yumenosora.connpass.com

その他採用情

虎の穴ラボでの開発に少しでも興味を持っていただけた方は、採用説明会やカジュアル面談という場でもっと深くお話しすることもできます。ぜひお気軽に申し込みいただければ幸いです。
カジュアル面談では虎の穴ラボのエンジニアが、開発プロセスの内容であったり、「今期何見ました?」といったオタクトークから業務の話まで何でもお応えします。

カジュアル面談や採用情報はこちらをご確認ください。
yumenosora.co.jp