今回はVue.jsのバリデーションライブラリである「VeeValidate」とコンポーネントテストライブラリの「Vue Test Utils」を使用してコンポーネントテストでバリデーションエラーを検証します。

「VeeValidate」を「Vue Test Utils」で使うためにはいくつか設定を行う必要があるので、記事の前半ではその設定内容を解説し、記事の後半で実際にテストを書いていきます。

設定

「VeeValidate」を「Vue Test Utils」で使用するために、まず以下の設定をjest.config.jsに追加します。

const config = {
  setupFiles: ['./tests/setup.js'],
  transform: {
    'vee-validate/dist/rules': 'babel-jest',
  },
  transformIgnorePatterns: [
    '<rootDir>/node_modules/(?!vee-validate/dist/rules)',
  ],
}

module.exports = config

次にVeeValidateのコンポーネントを読み込み、ルールをextendします。

setup.jsでもコンポーネントのテストファイルでもどちらに記述してもOKです。

import { ValidationObserver, ValidationProvider, extend } from 'vee-validate'
Vue.component('ValidationProvider', ValidationProvider);
Vue.component('ValidationObserver', ValidationObserver);

// ルールを使用している場合
import { required, email, alpha_num  } from 'vee-validate/dist/rules';
extend('required', required)
extend('email', email)
extend('alpha_num', alpha_num)

入力フォームのテスト

それではテストを実装していきます。

今回は、メールアドレス入力フォームでバリデーションを実装していると想定します。

バリデーションルールは、VeeValidateのemail ruleをそのまま使用していますが、入力された文字列がメールアドレスの形式でなければ「有効なメールアドレスではありません」とエラーメッセージが表示されます。

<validation-provider
    name="メールアドレス"
    :rules="{
        email: {},
        required: {}
    }"
    v-slot="{ errors }"
    tag="div"
    ref="email_provider"
>
    <v-text-field
        v-model="email"
        label="メールアドレス"
        hide-details
        outlined
        autofocus
        dense
        data-test-id="inputEmail"
    ></v-text-field>
    <div class="input-error-message">{{ errors[0] }}</div>
</validation-provider>

テスト仕様

メールアドレスの入力フォームのテストで確認したいことは下記です。

  1. コンポーネントをマウントする
  2. input要素に「e2etest」というメールアドレスではない文字列をセットする
  3. class=”input-error-message”の要素に「有効なメールアドレスではありません」が表示される
  4. v-slot=”{ errors }”にエラーメッセージが格納されている

テスト実装

vuetifyやVeeValidateなどの初期設定が正常に行われているのが前提条件ではありますが、下記のテストコードでバリデーションが正常に動作しているかをテストできます。

import Signup from '@/components/pages/auth/Signup.vue'
import Vuetify from 'vuetify'
import { createLocalVue, mount } from '@vue/test-utils'
import flushPromises from 'flush-promises';

describe('入力フォームテスト', () => {
  const localVue = createLocalVue()
  let vuetify

  beforeEach(() => {
    vuetify = new Vuetify()
  })
  
  it('バリデーションエラー email', async () => {
    const wrapper = mount(Signup, {
      localVue,
    })
    wrapper.get('[data-test-id="inputEmail"]').setValue("e2etest")
    await flushPromises();
    console.log(wrapper.get('.input-error-message').text());
    console.log(wrapper.vm.$refs.provider.errors[0]);
    expect(wrapper.get('.input-error-message').text()).toBe("有効なメールアドレスではありません");
    expect(wrapper.vm.$refs.email_provider.errors[0]).toBe("有効なメールアドレスではありません");
  });
});

テスト結果

console.log
    有効なメールアドレスではありません

      at Object.<anonymous> (tests/unit/auth/Signup.spec.js:153:13)

  console.log
    有効なメールアドレスではありません

 PASS  tests/unit/auth/Signup.spec.js
  Signup.vue
    ✓ バリデーションエラー email (106 ms)
    ✓ バリデーションエラーnull email (51 ms)
    ○ skipped 初期表示
    ○ skipped 入力テスト data側 値なし
    ○ skipped data値あり→input反映
    ○ skipped input入力→data反映
    ○ skipped 認証エラーメッセージ data側

Test Suites: 1 passed, 1 total
Tests:       5 skipped, 2 passed, 7 total
Snapshots:   0 total
Time:        0.911 s, estimated 1 s

解説

VeeValidateは非同期で実行される

まずVeeValidateのバリデーションチェックは非同期で実行されます。

つまり、下記のコードでinput要素にデータをセットした後に同期的にバリデーションエラーが表示されるわけではありません。

wrapper.get('[data-test-id="inputEmail"]').setValue("e2etest")

非同期処理が完了してから、エラーメッセージが表示されているかを確認しなければなりません。

非同期処理の完了を待つためには、$nextTickかflushPromisesプラグインを使用します。

今回はflushPromisesを使用して、非同期処理の完了を待ちます。

flushPromises

wrapper.get('[data-test-id="inputEmail"]').setValue("e2etest")
await flushPromises();
expect(wrapper.get('.input-error-message').text()).toBe("有効なメールアドレスではありません");
expect(wrapper.vm.$refs.email_provider.errors[0]).toBe("有効なメールアドレスではありません");

input要素に”e2etest”という文字列をセットして、非同期にDOMが更新されるのを待ちます。

DOMが更新されたら、エラーメッセージを表示する予定のHTML要素に「有効なメールアドレスではありません」が表示されているかを確認します。

ちなみに、VeeValidateのエラーメッセージはwrapper.vm.$refs.ref指定.errors[0]でも取得できます。

より正確にバリデーションエラーを検証するためにも、ダブルでチェックするといいかもしれませんね。

メールアドレスが正常に入力された場合

上記ではエラーをテストしましたが、実際に正しいメールアドレス形式で入力された場合のテストも行いましょう。

確認したいことは下記です。

  1. コンポーネントをマウントする
  2. input要素に「example@example.com」というメールアドレスをセットする
  3. class=”input-error-message”の要素は””(空文字である)
  4. v-slot=”{ errors }”はundefinedである
it.only('バリデーションエラーnull email', async () => {
    const mockFunction = jest.fn();
    const wrapper = mount(Signup, {
      localVue,
    })
    wrapper.get('[data-test-id="inputEmail"]').setValue("example@example.com")
    await flushPromises();
    console.log(wrapper.get('.input-error-message').text());
    console.log(wrapper.vm.$refs.provider.errors[0]);
    expect(wrapper.get('.input-error-message').text()).toBe("");
    expect(wrapper.vm.$refs.provider.errors[0]).toBeUndefined();
  });

無事にテストがパスしました。

  console.log

      at Object.<anonymous> (tests/unit/auth/Signup.spec.js:175:13)

  console.log
    undefined

      at Object.<anonymous> (tests/unit/auth/Signup.spec.js:176:13)

 PASS  tests/unit/auth/Signup.spec.js
  Signup.vue
    ✓ バリデーションエラー email (90 ms)
    ✓ バリデーションエラーnull email (46 ms)
    ○ skipped 初期表示
    ○ skipped 入力テスト data側 値なし
    ○ skipped data値あり→input反映
    ○ skipped input入力→data反映
    ○ skipped 認証エラーメッセージ data側

Test Suites: 1 passed, 1 total
Tests:       5 skipped, 2 passed, 7 total
Snapshots:   0 total
Time:        0.87 s, estimated 1 s

参照:テストに関する注意事項

カテゴリー: Vue.js