「Nuxt.jsビギナーズガイド」の感想・備忘録7

スポンサーリンク
「Nuxt.jsビギナーズガイド」の感想・備忘録6の続き

Vuexストアのテスト

  • 単一ファイルコンポーネントのテストにはJestプラグイン(vue-jest)が必要だが、まずピュアなJavaScriptだけで書かれたVuexストアのテストを体験してみる。
  • Vue Test Utilsを利用したテストでは、createLocalVue()でVueインスタンスを生成する必要がある。
  • Vuexを利用する場合は、Vueインスタンスのuseメソッドで Vuex を使用するように指示する必要がある。

1. 以下のapp/store/index.jsのテストを作成する場合

const state = () => ({
  count: 0
})

const getters = {
  count: (state) => state.count
}

const mutations = {
  increment(state) {
    state.count++
  }
}

const actions = {
  increment({ commit }) {
    commit('increment')
  }
}

module.exports = {
  state,
  getters,
  mutations,
  actions
}

2. 以下のspec/store/index.spec.jsを作成する

const Vuex = require('vuex')
const index = require('../../../app/store')
const { createLocalVue } = require('@vue/test-utils')
const cloneDeep = require('lodash.clonedeep')

const localVue = createLocalVue()
localVue.use(Vuex)

describe('store/index.js', () => {
  let store
  beforeEach(() => {
    store = new Vuex.Store(cloneDeep(index))
  })

  describe('mutations', () => {
    test('increment ミューテーションがコミットされると、 count ステートの値が +1 される)', () => {
      expect(store.getters['count']).toBe(0)
      store.commit('increment')
      expect(store.getters['count']).toBe(1)
    })
  })

  describe('actions', () => {
    test('increment アクションを dispatch するたびに、 increment ミューテーションがコミットされる', () => {
      expect(store.getters['count']).toBe(0)
      store.dispatch('increment')
      expect(store.getters['count']).toBe(1)
    })
  })
})

3. npm run testでテスト実行

単一ファイルコンポーネントのテスト

単一ファイルコンポーネントのテスト

  • vue/test-utilsのmount関数を使ってコンポーネントをマウントする。

1. 以下のapp/components/AppToggleButton.vueのテストを作成する場合

<template>
  <div>
    <p>{{status ? 'on' : 'off'}}</p>
    <button type="button" @click="toggle">toggle</button>
  </div>
</template>
<script>
export default {
  data() {
    return {
      status: false
    }
  },
  methods: {
    toggle() {
      this.status = !this.status
    }
  }
}
</script>

2. 以下のspec/components/AppToggleButton.spec.jsを作成する

import AppToggleButton from '~/components/AppToggleButton.vue'
import { mount } from '@vue/test-utils'

describe('AppToggleButton.vue', () => {
  let wrapper

  beforeEach(() => {
    wrapper = mount(AppToggleButton)
  })

  test('デフォルト状態で off であるか', () => {
    expect(wrapper.find('p').text()).toBe('off')
  })

  test('ボタンを押すことによって on となるか', async () => {
    await wrapper.find('button').trigger('click')
    expect(wrapper.find('p').text()).toBe('on')
  })
})

3. npm run testでテスト実行

Vue Routerを使っている単一ファイルコンポーネントのテスト

  • mount時にstoreとlocalVueを渡す必要がある。

1. 上述のapp/store/index.jsを使った以下のapp/pages/index.vueのテストを作成する場合

<template>
  <div>
    <h2>カウンター</h2>
    <h3>Count: <span class="count">{{count}}</span></h3>
    <button type="button" @click="increment">Increment</button>
    <AppToggleButton />
  </div>
</template>

<script>
import { mapGetters, mapActions } from 'vuex'
import AppToggleButton from '~/components/AppToggleButton.vue'
export default {
  components: {
    AppToggleButton
  },
  computed: {
    ...mapGetters(['count'])
  },
  methods: {
    ...mapActions(['increment'])
  }
}
</script>

2. 以下のspec/pages/index.jsを作成する

import { mount, createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
import IndexPage from '~/pages/index'
import store from '~/store/index'
import cloneDeep from 'lodash.clonedeep'

const localVue = createLocalVue()
localVue.use(Vuex)

describe('pages/index.vue', () => {
  let wrapper

  beforeEach(() => {
    wrapper = mount(IndexPage, {
      store: new Vuex.Store(cloneDeep(store)),
      localVue
    })
  })

  test('カウンターをクリックした時に、カウント値が 1 加算される', () => {
    expect(wrapper.vm.count).toBe(0)
    wrapper.find('button').trigger('click')
    expect(wrapper.vm.count).toBe(1)
  })
})

3. npm run testでテスト実行

Vue Routerを使っている単一ファイルコンポーネントのテスト

  • mount時にstubsオプションでvue-test-utilsのRouterLinkStubコンポーネントを指定する。

1. 以下のapp/pages/child.vueのテストを作成する場合

<template>
  <div>
    <nuxt-link to="/">トップページへ戻る</nuxt-link>
  </div>
</template>

<script>
export default {
}
</script>

2. 以下のspec/pages/child.jsを作成する

import { mount, createLocalVue, RouterLinkStub } from '@vue/test-utils'
import ChildPage from '~/pages/child'

const localVue = createLocalVue()

describe('pages/child.vue', () => {
  test('トップページへ戻る導線が存在する', () => {
    const wrapper = mount(ChildPage, {
      localVue,
      stubs: {
        NuxtLink: RouterLinkStub
      }
    })
    // 書籍ではfindだが非推奨のためfindComponentを使う
    expect(wrapper.findComponent(RouterLinkStub).props().to).toBe('/')
  })
})

3. npm run testでテスト実行

スナップショットテスト

  • コンポーネントの状態やレンダリング結果をシリアライズして保存し、それらに変化があるかどうかをテストすることができる。
  • 通常のテストとは異なり、1度テストを記述するだけで恒久的に利用でき、コマンドひとつで正しい状態を再生成できる。
  • とはいえ、精度が高いテストではないため、本当に必要かどうか考えて導入するべきである。

1. 上述のspec/components/AppToggleButton.spec.jsにexpect(wrapper.element).toMatchSnapshot()を追加する

import AppToggleButton from '~/components/AppToggleButton.vue'
import { mount } from '@vue/test-utils'

describe('AppToggleButton.vue', () => {
  let wrapper

  beforeEach(() => {
    wrapper = mount(AppToggleButton)
  })

  test('デフォルト状態で off であるか', () => {
    expect(wrapper.find('p').text()).toBe('off')
    expect(wrapper.element).toMatchSnapshot()
  })

  test('ボタンを押すことによって on となるか', async () => {
    await wrapper.find('button').trigger('click')
    expect(wrapper.find('p').text()).toBe('on')
    expect(wrapper.element).toMatchSnapshot()
  })
})

2. npm run testでテスト実行

  • 1度テストを実行するとspec/components/__snapshots__にスナップショットが生成され、2回目以降はそのスナップショットとの比較が行われる。
  • スナップショットの更新は、Jest実行時に–updateSnapshotオプションを付与する。

コメント