Vee-validate + Jest でバリデーションの単体テストを作成してみました



Vue.js を使った画面の入力フォームで、バリデーションの確認をするための単体テストを作成してみました。


*参考



*環境

  • MacOS
  • Vue 2.9.6
  • Vee-validate 2.2.0
  • Vue/test-utils 1.0.0-beta.29
  • Jest 24.5.0


*Vueプロジェクトの作成

Vueプロジェクトを作成してテストフレームワークのJestを導入します。
今回は下記記事の内容を行なった前提で進めていきます。









*Vee-validate の追加

下記コマンドを実行してインストールします。
$ npm install vee-validate --save

Vee-validate を使えるようにするためにmain.jsに import を追加します。vee-validateだけだとエラーメッセージが英語になるので、日本語で使えるようにlocale/jaも import します。
<main.js>
import Vue from 'vue'
import App from './App'
import router from './router'
import VeeValidate, { Validator } from 'vee-validate'  <--追加
import ja from 'vee-validate/dist/locale/ja'           <--追加

Vue.config.productionTip = false
Validator.localize('ja', ja)          <--追加
Vue.use(VeeValidate, { locale: ja })  <--追加

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'
})


*入力フォームの実装

日本語と数値の入力フォームにバリデーションを実装してみます。プロジェクト作成時のデフォルトのままだとVueのロゴが各画面に入ってしまうので、App.vueのロゴ画像を削除します。
<App.vue>
<template>
  <div id="app">
    <img src="./assets/logo.png">   <-- 削除
    <router-view/>
  </div>
</template>

<script>
export default {
  name: 'App'
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

入力フォームを実装するために、新規画面を追加します。
data-vv-as=に設定した文字列がエラーメッセージの主語に反映されます。
validate=に追加したいバリデーションの種類を設定します。今回は「名前」については必須と最大10桁のチェック、「数値」については必須と数字、1〜15の間チェックを追加しました。
<components/Form.vue>
<template>
  <div class="form-content">
    <div>
      <label>名前</label>
      <input type="text" name="name1" data-vv-as="名前" v-validate="'required|max:10'"/>
      <span v-show="errors.has('name1')" class="alert-label">{{ errors.first('name1') }}</span>
    </div>
    <div>
      <label>数値</label>
      <input type="text" name="num1" data-vv-as='数値' v-validate="'required|numeric|between:1,15'" />
      <span v-show="errors.has('num1')" class="alert-label">{{ errors.first('num1') }}</span>
    </div>
  </div>
</template>

<script>
export default {
}
</script>

<style scoped>
.form-content {
  margin-left: 100px;
  text-align: left;
}
.alert-label {
  color: red;
  font-size: 12px;
}
</style>

新規作成した画面にアクセスできるようにするため、index.jsに path を追加します。
デフォルトのままだとアクセスした際にURLに「#」が入ってしまうので、入らないようにmode: 'history'の設定を追加します。
<router/index.js>
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
import Form from '@/components/Form'  <-- 追加

Vue.use(Router)

export default new Router({
  mode: 'history',  <-- 追加
  routes: [
    {
      path: '/',
      name: 'HelloWorld',
      component: HelloWorld
    },
    // ↓追加ここから
    {
      path: '/form',
      name: 'Form',
      component: Form
    }
    // ↑追加ここまで
  ]
})

下記コマンドを実行してWebアプリケーションを起動します。
npm run dev











入力フォームにエラーになるような値を入力すると、エラーメッセージが表示されます。











*テストを追加

Vueの公式テストライブラリtest-utilsが用意されていて、これを使うと通常よりも少し便利にテストの実装ができるようになります。
下記コマンドを実行してtest-utilsをインストールします。
npm i @vue/test-utils

デフォルトで作成されている/test/unit/specs/ディレクトリに新規ファイルForm.spec.jsを追加します。
vee-validateを使うために、createLocalVueでグローバルプラグインのコンストラクタを作成する必要があります。
テスト1つごとにit()で定義し、引数にテスト名を設定します。
shallowMountを使うと子コンポーネントをスタブとして使うことができ、コンポーネントの要素にアクセスできるようになります。
また、sync: falseのオプションを設定することで非同期で描画させることができます。
</test/unit/specs/Form.spec.js>
import { shallowMount, createLocalVue } from '@vue/test-utils'
import VeeValidate, { Validator } from 'vee-validate'
import Form from '@/components/Form'
import ja from "vee-validate/dist/locale/ja";

// Vueコンストラクタを作成
const localVue = createLocalVue()
Validator.localize('ja', ja)
// プラグインをインストール
localVue.use(VeeValidate, { locale: ja })

describe('Form.vue', () => {
  // バリデーションエラーのテスト開始
  it('validate required error message', async () => {
    // コンポーネントのスタブを作成
    const wrapper = shallowMount(Form, { localVue, sync: false })

 // 要素に値を設定
    wrapper.find('[name="name1"]').setValue('')
    wrapper.find('[name="num1"]').setValue('')
    // バリデートを実行
    await wrapper.vm.$validator.validateAll()

 // 実行結果の確認
    expect(wrapper.vm.errors.first('name1')).toEqual('名前は必須項目です')
    expect(wrapper.vm.errors.first('num1')).toEqual('数値は必須項目です')
  })
})

下記コマンドを実行してテストを実施します。
$ npm run unit

実行結果が出力され、カバレッジを見ることができます。
 PASS  test/unit/specs/Form.spec.js
  Form.vue
    ✓ validate required error message (39ms)

 PASS  test/unit/specs/HelloWorld.spec.js
  HelloWorld.vue
    ✓ should render correct contents (9ms)

----------------|----------|----------|----------|----------|-------------------|
File            |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
----------------|----------|----------|----------|----------|-------------------|
All files       |      100 |      100 |      100 |      100 |                   |
 HelloWorld.vue |      100 |      100 |      100 |      100 |                   |
----------------|----------|----------|----------|----------|-------------------|
Test Suites: 2 passed, 2 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        1.877s
Ran all test suites.


*所感

バリデーションのテストケースを異なるパターンで大量に書くと見辛くなるので、もう少し綺麗に書く方法を見つけたいです。
バリデーションのテスト以外にもAPIをモックにする方法なども試してみたいと思います。


Previous
Next Post »

人気の投稿