Open6

Jest+Mock Service Worker(msw)でNuxt.jsのstoreをテストしてみる

Yoshiyuki HisamatsuYoshiyuki Hisamatsu

package.json をチラ見して気がついたんですが、

以下のコマンドを打ったときに、

$ npx msw init static/ --save

こんな感じに自動で追記されてるんですねー

  "msw": {
    "workerDirectory": "static"
  }
Yoshiyuki HisamatsuYoshiyuki Hisamatsu

Jest でモックを使うための下準備をする

mocks/server.js

// src/mocks/server.js
import { setupServer } from 'msw/node'
import { handlers } from './handlers'
// This configures a request mocking server with the given request handlers.
export const server = setupServer(...handlers)

ブラウザーから使う場合は、 browser.js を追加しましたが、それの node.js 版をここで作成します。

https://mswjs.io/docs/getting-started/integrate/node#configure-server

setup/setup.ts

ディレクトリ名は、いつもこうゆう感じにしているので Nuxt の命名っぽくないかもしれません。

必ずモックサーバーを使われるように、ここでセットアップしておきます。

// src/setupTests.js
import { server } from '../mocks/server'
// Establish API mocking before all tests.
beforeAll(() => server.listen())
// Reset any request handlers that we may add during the tests,
// so they don't affect other tests.
afterEach(() => server.resetHandlers())
// Clean up after the tests are finished.
afterAll(() => server.close())

jest.config.js に setupFilesAfterEnv を設定する

module.exports = {
  moduleNameMapper: {
    '^@/(.*)$': '<rootDir>/$1',
    '^~/(.*)$': '<rootDir>/$1',
    '^vue$': 'vue/dist/vue.common.js',
  },
  moduleFileExtensions: ['ts', 'js', 'vue', 'json'],
  transform: {
    '^.+\\.ts$': 'ts-jest',
    '^.+\\.js$': 'babel-jest',
    '.*\\.(vue)$': 'vue-jest',
  },
  collectCoverage: true,
  collectCoverageFrom: [
    '<rootDir>/components/**/*.vue',
    '<rootDir>/pages/**/*.vue',
  ],
  setupFilesAfterEnv: ['./__setup__/setup.ts'],  // ここです!
};
Yoshiyuki HisamatsuYoshiyuki Hisamatsu

テストコードを書く

あくまでも store の部分のみで、 API との通信は msw がモックしてくれているという状態です。

Vue の部分を絡まないテストですが、この store の部分は担保しておきたい!という場合には有効なのかなーと思います。

test/store.test.ts

import { createLocalVue } from '@vue/test-utils';
import axios from 'axios';
import Vuex from 'vuex'
import * as storeIndex from '@/store/index'

// Vue のセットアップ
const localVue = createLocalVue();
localVue.use(Vuex);

describe('storeIndex tests', () => {
  let store: any;

  beforeEach(() => {
    // @ts-ignore
    store = new Vuex.Store(storeIndex)
    // これをしておかないと this.$axios が使えない
    store.$axios = axios;
  })

  test('apiのレスポンス0のテスト', async (done) => {
    // @ts-ignore
    await store.dispatch('fetchApi')
    // getters 経由でアクセスする場合
    expect(store.getters['getResult'].name).toBe('foo');
    // 非同期のテストは done しないとずっと完了してくれない
    done();
  });

  test('apiのレスポンス1のテスト', async (done) => {
    // @ts-ignore
    await store.dispatch('fetchApi')
    // state 経由でアクセスする場合
    expect(store.state.result[1].name).toBe('bar');
    done();
  });
});

https://zenn.dev/captain_blue/articles/test-vuex-with-jest

https://zenn.dev/ryo_kawamata/articles/introduce-mock-service-worker