Help us understand the problem. What is going on with this article?

VeeValidateを利用したコンポーネントをJestでテストする

はじめに

Vue v3及び、CompositionAPIで作られたコンポーネントでvee-validate v3系を対象に、Jestでテストを書きます

以下のようなコンポーネントを想定

  • validationProviderでinputをvalidate
  • validationObserverで各Providerを監視
child-input.vue
<template>
  <validation-provider mode="eager" rules="required">
    <input
      v-model="val"
      name="input"
    >
    <ul v-if="errors.length" class="errors">
      <li v-for="(error, i) in errors" :key="i">
        {{ error }}
      </li>
    </ul>
  </validation-provider>
</template>

<script lang="ts">
export default {
  setup(_, { emit }) {
    const val = ref<string>('')
    watch(() => {
      emit('update', val.value)
    })
    return { val }
  }
}
</script>
parent-form.vue
<template>
  <validation-observer ref="observer">
    <form>
      <child-input @update="update" />
    </form>
  </validation-observer>
</template>

<script lang="ts">
export default {
  setup(_, { emit }) {
    const val = ref<string>('')
    const update = (value: string) => {
      val.value = value
    }
    return {
      val,
      update
    }
  }
}
</script>

テスト

それではテストを書いていきます

form-validation.spec.ts
import { createLocalVue, mount } from '@vue/test-utils'
import { ValidationObserver, ValidationProvider, extend } from 'vee-validate'
import ParentForm from '@your/path/parent-form.vue'

const localVue = createLocalVue()
localVue.component('ValidationObserver', ValidationObserver)
localVue.component('ValidationProvider', ValidationProvider)

const { required } = require('vee-validate/dist/rules.umd')
extend('required', required)

jest.useFakeTimers()

describe('フォームのバリデーションチェック', () => {
  test('未入力でバリデーションされること', async (): Promise<any> => {
    const wrapper = mount(ParentForm, { localVue })
    const observer: InstanceType<typeof ValidationObserver> = wrapper.vm.$refs.observer
    await observer.validate()
    jest.runAllTimers()
    await wrapper.vm.$nextTick()
    expect(wrapper.find('.errors').exists()).toBeTruthy()
  })
  test('入力が完了すること', async (): Promise<any> => {
    const wrapper = mount(ParentForm, { localVue })
    wrapper.find('input').setValue('hoge')
    const observer: InstanceType<typeof ValidationObserver> = wrapper.vm.$refs.observer
    await observer.validate()
    jest.runAllTimers()
    await wrapper.vm.$nextTick()
    expect(wrapper.find('.errors').exists()).toBeFalsy()
  })
})

テストの解説

それぞれの処理について見ていきます。

const wrapper = mount(ParentForm, { localVue })

今回は子コンポーネントもテスト対象のためshalowMountではなくmountを利用しています。古い記事を参照するとmountのオプションに{ sync: false }を追加するという情報がありますがそのオプションは廃止されています。
https://github.com/vuejs/vue-test-utils/issues/1137

const observer: InstanceType<typeof ValidationObserver> = wrapper.vm.$refs.observer
await observer.validate()

ValidationObserverインスタンスを取得してvalidateメソッドを実行します。これによってvee-validateのバリデーションが実行されます。

jest.runAllTimers()

ValidationObserverの処理に16msかかるそうで、それを待ちます。
https://logaretm.github.io/vee-validate/advanced/testing.html#testing-validationobserver-debounced-state

await wrapper.vm.$nextTick()

先程{ sync: false }オプションがなくなったことで画面描画を待つために、$nextTickを利用します。このメソッドがPromiseを返すためawaitで待つことによってこの後の処理ではDOMに反映された状態を取得することができます。
vee-validate公式ではflush-promisesを使う例を紹介されていますが$nextTickを利用することで同様の結果を得られるので不要になるかと思います。

まとめ

  • syncオプションはもう使えない
  • flush-promisesではなく$nextTickを使おう
kubotak
フロントエンドが好物 これでもサーバーサイドエンジニア
https://kubotak.page
macloud
M&Aクラウドは「テクノロジーの力で、M&Aに流通革命を」をミッションにM&Aプラットフォーム「M&Aクラウド」を開発運営するスタートアップです。
https://macloud.jp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした