前回(JestでNuxt+Vuetifyのテストを書いてみる)に続き、今回は初期表示の正常系テストを書いてみました。
最初に対応する時に役立つのでメモしておきます。

injectしている関数のMockを作らなくても動くようにする

前回(pluginでinjectしている関数のテスト)では、使用するテストファイル毎にMockを作っていましたが、手間なのと期待値の確認(表示されたか)もしやすくなので、どこでも関数が呼ばれるようにする事にしました。

test/setup.js に追加

import Vue from 'vue'
import Vuetify from 'vuetify'
import { TestPluginUtils } from '~/plugins/utils.js'

Vue.use(Vuetify)
Vue.use(TestPluginUtils)

plugins/utils.js のTestPluginを修正

export const TestPluginUtils = {
  install (Vue) {
    Vue.prototype.$dateFormat = dateFormat
    Vue.prototype.$timeFormat = timeFormat
    Vue.prototype.$pageFirstNumber = pageFirstNumber
    Vue.prototype.$pageLastNumber = pageLastNumber
  }
}

呼び出し元

下記のように定義していたmocksが不要になりました。

test/page/users/undo_delete.spec.js

    return shallowMount(Page, {
      localVue,
      vuetify,
      mocks: {
-        $dateFormat: jest.fn(),
-        $timeFormat: jest.fn()

i18nの値がネスト(入れ子)だと取れない

前回作成した($tが無いと言われる)ではネスト(入れ子)にした連想配列だと、undefinedになってしまうバグがありました。

こんな感じで定義していて、

locales/ja.js

  network: {
    failure: '通信に失敗しました。しばらく時間をあけてから、やり直してください。',
    error: '通信エラーが発生しました。しばらく時間をあけてから、やり直してください。'
  },

テストで使うとundefinedに。

    Expected: "通信に失敗しました。しばらく時間をあけてから、やり直してください。"
    Received: undefined

test/setup.js を修正

import Vue from 'vue'
import Vuetify from 'vuetify'
import { config, RouterLinkStub } from '@vue/test-utils'
import { TestPluginUtils } from '~/plugins/utils.js'
import locales from '~/locales/ja.js'

Vue.use(Vuetify)
Vue.use(TestPluginUtils)

// Mock i18n
config.mocks = {
  $t: (key) => {
    let locale = locales
    const parts = key.split('.')
    for (const part of parts) {
      locale = locale[part]
    }
    // eslint-disable-next-line no-throw-literal
    if (locale == null) { throw 'Not found: i18n(' + key + ')' }
    return locale
  }
}

// Stub NuxtLink
config.stubs.NuxtLink = RouterLinkStub

NuxtLinkのパスが存在するかのテスト

RouterLinkStubのprops().toで取れますが、複数ある場合が多いので、先に一覧化して、含まれるかをテストするようにしました。
また、よく使うのでclass作って、importしています。

test/page/infomations/index.spec.js

import { Helper } from '~/test/helper.js'
const helper = new Helper()
      const links = helper.getLinks(wrapper)
        expect(links.includes('/infomations/' + list.id)).toBe(list.body_present) // [本文あり]お知らせ詳細

test/helper.js

import { RouterLinkStub } from '@vue/test-utils'

export class Helper {
  // NuxtLinkのURL一覧を配列で返却
  getLinks = (wrapper) => {
    const routerlinks = wrapper.findAllComponents(RouterLinkStub)
    const links = []
    for (let i = 0; i < routerlinks.length; i++) {
      const link = routerlinks.at(i).props().to
      if (link.name === 'infomations-id___ja') {
        links.push('/infomations/' + link.params.id) // お知らせ一覧
      } else {
        links.push(link)
      }
    }
    return links
  }
}

> <NuxtLink to="/infomations">

toがlinksに含まれるかでテストできます。

> <NuxtLink :to="{ name: 'infomations-id___ja', params: { id: list.id }}">

ただ、こんな感じにパラメータを渡している場合、props().toで取れる値には下記のように設定されますが、includesだとtrueにならないので、linksに入れる時に実際のパスを保存するように、テストも実際のパスで行っています。

> { name: 'infomations-id___ja', params: { id: 1 } }

async/awaitの挙動について

下記のように、awaitの後に処理があった場合、

pages/users/delete.vue

  async created () {
    try {
      await this.$auth.fetchUser()
    } catch (error) {
      if (error.response == null) {
        this.$toasted.error(this.$t('network.failure'))
      } else if (error.response.status === 401) {
        return this.appSignOut()
      } else {
        this.$toasted.error(this.$t('network.error'))
      }
      return this.$router.push({ path: '/' })
    }

    if (!this.$auth.loggedIn) {
      return this.appRedirectAuth()
    }
    if (this.$auth.user.destroy_schedule_at !== null) {
      return this.appRedirectDestroyReserved()
    }

    this.processing = false
    this.loading = false
  },

下記のようにテストを書くと、上のawaitの所までの状態となり、
その後(今回はloadingがfalse)を期待したテストが失敗します。
console.log(wrapper.html())も途中の状態が表示されます。

test/page/users/delete.spec.js

    await expect(authFetchUserMock).toBeCalledTimes(1)
    expect(wrapper.findComponent(Loading).exists()).toBe(false)

sleepして対応

毎回定義するのは手間なので、上で作成したhelperに追加しました。

import { Helper } from '~/test/helper.js'
const helper = new Helper()
    await helper.sleep(1)
    expect(authFetchUserMock).toBeCalledTimes(1)
    expect(wrapper.findComponent(Loading).exists()).toBe(false)

await/sleepの前は、awaitの前の状態のテスト、
後は最終的な状態のテストを書けるようになります。

test/helper.js

export class Helper {
  // 一定時間停止
  sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
}

今回のコミット内容
https://dev.azure.com/nightonly/nuxt-app-origin/_git/nuxt-app-origin/commit/b82ac30c676913c30059aa90a670369a3700df97

JestでNuxt+Vuetifyのテストを書く時のTips #1” に対して1件のコメントがあります。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です