📚

【Nuxt.js】VuexのactionsをJestでテストする

3 min read

Vuexのテストするポイント

  • actionsでテストする内容
    • 想定しているAPIのエンドポイントにアクセスしているか
    • APIに渡している値が正しいか
    • 想定しているmutateにコミットしているか

ソース

ソース

actions内で非同期処理をしている場合

  • ittestメソッドの第2引数にasyncをつける必要がある
index.spec.ts
describe('index store', () => {
  describe('actions', () => {
    it('should fetchUser', async () => {
      const commit = jest.fn();
      const $axios

      await actions.fetchUser({ commit });
    });
  });
});
  • axiosをmockにしておく
index.spec.ts
let url = ''
let body = {}
let mockError = false

jest.mock("axios", () => ({
  post: (_url, _body) => {
    return new Promise((resolve) => {
      if (mockError)
        throw Error("Mock error")

      url = _url
      body = _body
      resolve(true)
    })
  }
}))

actionsの内部でthis.$axiosでaxiosにアクセスできるのは、Nuxt.jsで@nuxtjs/axiosを使っているためなので、mockメソッドの第一引数はaxiosでよい

  • mockしたaxiosをstoreにセットする
  1. テスト対象となるstore配下のファイルをimportする
  2. localVueにVuexを設定する
  3. 1でimportしたファイルをStoreとしてオブジェクトを生成
  4. 3でつくったStoreにモックしたaxiosを設定する
  5. actionsを呼び出す際は、dispatch(xxx)を使う
index.spec.ts
import { createLocalVue } from '@vue/test-utils'; // 2.でlocalVueを生成するために必要
import axios from 'axios';
import { cloneDeep } from 'lodash';
import Vuex from 'vuex';
import ROUTES from '~/routes/api';
import * as storeIndex from '~/store/index'; // 1. テスト対象となるstore配下のファイルをimportする

// axiosのmock

const localVue = createLocalVue();
localVue.use(Vuex); // 2. localVueにVuexを設定する

describe('index store', () => {
  describe('actions', () => {
    let store: any;

    beforeEach(() => {
      store = new Vuex.Store(cloneDeep(storeIndex as any)); // 3. 1でimportしたファイルをStoreとしてオブジェクトを生成
      store.$axios = axios as NuxtAxiosInstance; // 4. 3でつくったStoreにモックしたaxiosを設定する
      store.commit = jest.fn();
      store.$router = { push: jest.fn() };
    });

    test('fetchUser should call user api and mutateUser', async () => {
      await store.dispatch('fetchUser'); // 5. actionsを呼び出す際は、`dispatch(`xxx`)`を使う
      expect(url).toBe(ROUTES.GET.USER);

      store.commit('mutateUser', user);
      expect(store.commit).toHaveBeenCalledWith('mutateUser', user);
    });
  });
});

3.の手順でlodashのcloneDeepを使って、storeを生成。
cloneDeepは、再帰的にディープコピーしてくれる

store.$axios = axios as NuxtAxiosInstance; // 4. 3でつくったStoreにモックしたaxiosを設定する
型アサーションにしてますが、これでいいのかまではあまりわかってません・・・

残課題

mutateはactions内で呼び出しているので、store.commit('mutateUser', user);をする必要はないはずですが、どうしてもmutateが呼び出されているのをどう書けばいいかがわからなかったです。。storeの_mutationsにあるのはわかったので、

expect(store._mutations.mutateUser).toHaveBeenCalledWith('mutateUser',{})

これでできないかなーとやってみたんですがうまくいかず。
「こうするんだよ」というのをご存知の方いれば教えて下さい!

参考資料

NuxtアプリケーションをJestでテストする - アクトインディ開発者ブログ
vue-testing-handbook/actions.spec.js at master · lmiller1990/vue-testing-handbook
Using with Vuex | Vue Test Utils
testing-vuex-store-example/store-config.spec.js at master · eddyerburgh/testing-vuex-store-example
Vue Testing Handbook
ディープコピーとシャローコピーの違い - ITを分かりやすく解説

Discussion

ログインするとコメントできます