Vuetify
本家のドキュメントのtestに関する章を参照
https://vuetifyjs.com/ja/getting-started/unit-testing/
Vuetifyの組み込み方は、specの最初に、次のように書く。
import Vue from 'vue'
import Vuetify from 'vuetify'
Vue.use(Vuetify)
本家のドキュメントに次のような記述がありますが、この方法でlocalVueにvuetifyを読み込んでもうまくいきませんでした。
//うまくいかなかった方法
let vuetify
beforeEach(() => {
vuetify = new Vuetify()
})
it('should have a custom title and match snapshot', () => {
const wrapper = mount(CustomCard, {
localVue,
vuetify,
propsData: {
title: 'Foobar',
},
})
Veevalidate
今回使用したのはVeevalidate 3.3.9 です。2系だと記述が違うので気を付けてください。
本家のドキュメントのtestに関する章を参照
https://logaretm.github.io/vee-validate/advanced/testing.html#asynchronous-testing
次のような記述例がありますが、$refを使った方法はうまく動作しませんでした(テンプレートもドキュメントと同じものにしてみたんですが…)
const error = wrapper.vm.$refs.provider.errors[0];
実際のinputやエラーを出力するDOMを見つけて値セットしたり、エラーメッセーを確認する方法は動作しました。以下の方法を参照してください。
値のセット方法
次のテンプレートの場合
<ValidationProvider name="email" v-slot="{ errors }" id="ID1" rules="required|email">
<v-text-field
id="input_email"
v-model="email"
:error-messages="errors"
label="E-mail"
required
></v-text-field>
</ValidationProvider>
次のような方法で、値をセットして、エラーメッセージを確認できます
inputにIDを付けて直接見つける方法
wrapper.find('input_email').setValue('aaa');
ValiidationProviderにIDを付けて見つける方法
(エラー出力を得る方法と統一できるのでお勧め)
wrapper.find('#ID1').find('input').setValue('aaa');
バリデーション結果のチェック方法
const errorEl = wrapper.find('#ID1').find('.v-messages__message');
expect(errorEl.text()).toBe('Email must be valid');
Vuetifyが組み込まれているかの確認方法
どこまで動作してるのか全く掴めず、デバックに非常に苦労しました。
Vuetifyが取り込まれていないとレンダリングが実行されず、バリデーションの動作が成功するはずもありません。
動作確認として、次のコードを含んだテストを実行して、作成されたスナップショットファイルを確認する(スナップショットファイルが存在しない時は、結果をファイルに保存する動作になる。存在する時は、比較する動作)のがお勧めです
expect(wrapper.html()).toMatchSnapshot()
※余談
スナップショットを更新(正解データを更新する)時には、次のコマンドを実行
npm run test:unit -- --updateSnapshot
エラーと対処方
SyntaxError: Unexpected token 'export'
Veevalidateや、Vuetifyのモジュールの中で、SyntaxErrorが出ます。
これは、jestがTypescriptの記述を認識していないために発生しています。
paclage.jsonのtransformIgnorePatternsに"/node_modules"のtransformを行わない設定がある為に起きています。この行を削除するか、次の例のように、vurtifyとvee-validateディレクトリは除く記述にします。
"jest": {
"transformIgnorePatterns": [
"<rootDir>/node_modules/(?!(vuetify|vee-validate))"
],
"preset": "@vue/cli-plugin-unit-jest"
}
specファイル全体
import Vue from 'vue'
import Vuetify from 'vuetify'
Vue.use(Vuetify)
import flushPromises from 'flush-promises';
import { mount, createLocalVue } from '@vue/test-utils'
const localVue = createLocalVue()
import AddEmail from '@/views/AddEmail.vue'
describe('AddEmail.vue', () => {
// バリデーションエラーのテスト開始
const mount_AddEmail = function() {
return mount(AddEmail, {
localVue,
sync: false
})
}
it('snapshot check', async () => {
const wrapper = mount_AddEmail()
expect(wrapper.html()).toMatchSnapshot()
})
it('dupcheck error if input "mike" in input of name', async () => {
const wrapper = mount_AddEmail()
wrapper.find('#ID2').find('input').setValue('mike');
await flushPromises();
const errorEl = wrapper.find('#ID2').find('.v-messages__message');
expect(errorEl.text()).toBe('同じ名前は登録できません');
})
it('email address format check', async () => {
const wrapper = mount_AddEmail()
wrapper.find('#ID1').find('input').setValue('aaa');
await flushPromises();
const errorEl = wrapper.find('#ID1').find('.v-messages__message');
expect(errorEl.text()).toBe('Email must be valid');
})
it('required condition of name', async () => {
const wrapper = mount_AddEmail()
wrapper.find('#ID2').find('input').setValue('');
await flushPromises();
const errorEl = wrapper.find('#ID2').find('.v-messages__message');
expect(errorEl.text()).toBe('name can not be empty');
})
it('required condition of email', async () => {
const wrapper = mount_AddEmail()
wrapper.find('#ID1').find('input').setValue('');
await flushPromises();
const errorEl = wrapper.find('#ID1').find('.v-messages__message');
expect(errorEl.text()).toBe('email can not be empty');
})
})