概要

Vuetify は Vue.js 用のコンポーネントフレームワークです。
今回はこの Vuetify のテキストフィールドを含むコンポーネントのユニットテストの書き方について紹介します。

題材となるコンポーネント

<script>
  export default {
    name: "ExampleInputForm",
    data() {
      return {
        word: "",
      };
    },
  };
</script>
<template>
  <v-form>
    <v-text-field v-model="word"></v-text-field>
    <v-btn @click="$emit('submit', word)"></v-btn>
  </v-form>
</template>

単純なテキストフィールドにボタンを用意しました。

テキストフィールドに入力された値はv-modelによってwordプロパティに格納されます。
ボタンが押されるとイベントをトリガーしてwordプロパティを親コンポーネントに渡します。

なお、後ほどテキストフィールドで利用できるバリデーション機能のテストも行います。
そのため、v-formタグを使って囲っています。

入力された値が正しく渡されているかテストする

まずはボタンが押された時にフォームに入力された値が渡されているかどうかを検証するテストを書きます。

import { mount, createLocalVue } from "@vue/test-utils";
import Vue from "vue";
import Vuetify from "vuetify";
import ExampleInputForm from "@/components/ExampleInputForm.vue";

const localVue = createLocalVue();
Vue.use(Vuetify);

describe("ExampleInputForm", () => {
  let vuetify;

  const build = () => {
    const wrapper = mount(ExampleInputForm, { localVue, vuetify });

    return {
      wrapper,
      input: () => wrapper.find("input"),
      button: () => wrapper.find("button"),
    };
  };

  beforeEach(() => {
    vuetify = new Vuetify();
  });

  test("イベントトリガー時に入力された値が渡されているか", () => {
    const expectedWord = "stardustkids";
    const { wrapper, input, button } = build();

    input().setValue(expectedWord);
    button().trigger("click");

    expect(wrapper.emitted().submit[0]).toEqual([expectedWord]);
  });
});

最初の 20 行くらいまではテストのセットアップを行なっています。
注意すべきは Vuetify を読み込んでいる箇所です。

テストコードの中で Vuetify を読み込む場合は通常と異なるやり方で行います。
詳しくはこちらの公式ドキュメントでも解説されています。

後半では、テキストフィールドに値をセットしてからボタンのクリックイベントを発行し、最後に値の検証を行なっています。

フォームのバリデーションをテストする

Vuetify のテキストフィールドにはバリデーション機能が存在します。
次はこれを使ったコンポーネントのテストを書いていきます。

まずは題材のコンポーネントにバリデーションを実装し、これをパスしないとボタンを押してもイベントが発行されないように変更を加えます。

<script>
  export default {
    name: "ExampleInputForm",
    data() {
      return {
        word: "",
        valid: true,
        rules: [(v) => !!v || "入力されていません。"],
      };
    },
  };
</script>

<template>
  <v-form v-model="valid">
    <v-text-field v-model="word" :rules="rules"></v-text-field>
    <v-btn @click="$emit('submit', word)"></v-btn>
  </v-form>
</template>

バリデーションをパスしたかどうかを表すvalidプロパティとバリデーションの内容となるrulesプロパティを追加しています。

import flushPromises from "flush-promises";
// ...
describe("ExampleInputForm", () => {
  //...
  test("何も入力されていなければイベントをトリガーしない", async () => {
    const { wrapper, button } = build();

    button().trigger("click");
    await flushPromises();

    expect(wrapper.vm.valid).toBeFalshy;
  });
});

バリデーションの検証は非同期で実行されます。

普通にテストを書くと、バリデーションの検証処理の前にアサーションが実行されてしまいテストがうまくいきません。
そこで、非同期で行われる処理が解決されるまで待つように、flushPromisesを使っています。

こうしてテストを実行すればきちんと通っているはずです。
何も入力しないでボタンを押すと、バリデーションの実行によってvalidプロパティがfalseへ変化しています。