概要
Vue.js(Vue CLI v4系)で、wrap = shallowMount() 越しに次のことをテストする方法について記載する。
- 子コンポーネント(Component)からevent upされた状態を検証する方法
- 対象のVueコンポーネントからevent upしたことを検証する方法
内容としては、公式サイトのガイドやAPI仕様書に記載されている通りなのだが、私はそれだけでは「実際にどのようにするのか?」を読み取れなかったので、その観点でのメモが目的。
対象とする被テストのコンポーネント
例として、画像ファイルを登録する前の説明ページを使う。
「ファイルの取り扱い」ダイアログを表示して、
その後に「同意する」チェックボックスを押した場合にのみ、
「画像を登録」ボタンを押せる仕様、とする。
実際の画像登録は、親コンポーネントへemit()して処理するものとし、
被テストのコンポーネントでは取り扱わない。
<template>
<div>
<div id="id_button_confirm_privacy" v-on:click="openModalOfPrivacy">
<span>【登録された画像ファイルの取り扱いについて】</span>
</div>
<!-- コンポーネント ModalConfirmPrivacy -->
<ModalConfirmPrivacy id="id_modaldialog_pivacy" v-on:close="closeModalOfPrivacy" v-if="isModalDialog">
<h3 slot="header">登録ファイルの取り扱いについてダイアログ</h3>
<span slot="body">
ここに任意のテキストを入れる。<br>
</span>
</ModalConfirmPrivacy>
<br>
<div id="id_checkbox_agree_privacy" v-on:click="toggleAgree">
<span id="id_agree2policy" v-show="isAgree">[レ]</span>
<span id="id_not_agree2policy" v-show="!isAgree">[ _ ]</span>
<span>「ファイルの取り扱い」に同意する</span>
</div>
<br>
<div id="id_button_select_image" v-on:click="go2Next">
【画像を登録】
</div>
</div>
</template>
<script>
// javascriptファイルをココへ配置
import ModalConfirmPrivacy from './ModalConfirmPrivacy.vue';
export default {
name : "Explanation",
components : { ModalConfirmPrivacy },
data : function () {
return {
isModalDialog: false,
isHavingSeen : false,
isAgree : false
}
},
methods : {
openModalOfPrivacy : function () {
this.isModalDialog = true;
},
closeModalOfPrivacy : function () {
this.isModalDialog = false;
this.isHavingSeen = true;
},
toggleAgree : function () {
this.isAgree = (this.isHavingSeen) ? !this.isAgree : false;
},
go2Next : function () {
if(this.isAgree){
this.$emit('go2SelectImage')
}
},
}
}
</script>
<style scoped>
/* Cssファイルはここへ配置する。 */
#id_button_confirm_privacy {
cursor: pointer;
text-decoration: underline;
}
#id_button_select_image {
cursor: pointer;
text-decoration: underline;
}
</style>
以下の環境で動作検証
"vue": "^2.6.11"
"@vue/cli-plugin-babel": "~4.2.0",
"@vue/cli-plugin-eslint": "~4.2.0",
"@vue/cli-plugin-unit-mocha": "~4.2.0",
"@vue/cli-service": "~4.2.0",
"@vue/test-utils": "^1.0.0-beta.29",
"babel-eslint": "^10.0.3",
"chai": "^4.1.2",
"eslint": "^6.7.2",
"eslint-plugin-vue": "^6.1.2",
"vue-template-compiler": "^2.6.11"
※なお、vue create
でインストールされる
@vue/test-utils@1.0.0-beta.31
だとtrigger() でDOMが更新されないように見える。
@vue/test-utils@1.0.0-beta.29
だと問題ない。
なので、vue create
した後で、npm install @vue/test-utils@1.0.0-beta.29 --save-dev
にて上書きしてダウングレードインストールして動作検証した。
※↑の理解、誤り。DOMの更新は、$nextTick()をもって待たねばならないのが仕様。
beta.29で動作したのは偶々だろう。
たとえば次のようにPromise(もしくはawiteショートハンド)してから先に進めばよい。
後で記事を修正する。
// DOM変化(v-show, v-if)を伴った応答をテストするには、DOMの更新後を待つ必要がある。
const waitVm = function (wrapper) {
return new Promise((resolve)=>{
wrapper.vm.$nextTick(()=>{
resolve();
})
})
};
子コンポーネントからのevent upを、スタブを用いてエミュレートする
Explanation.vueをshallowMount()でwrapper生成して、
コンポーネントModalConfirmPrivacyの close
イベントに紐づけられた
closeModalOfPrivacy()
が呼ばれた状態をエミュレートするには、
次にようにする。
- shallowMount() に stabs オプションを指定して、「closeイベントを$emitするノードを持ったスタブ」を設定する
- スタブ内のノードを trigger()して $emitを経由してcloseイベントを発火する
これにより、closeModalOfPrivacy()
を直に呼び出すのではなく、
あくまでも close
イベントに紐づけられたソレを呼び出した状態、を検証できる。
具体的なテストコードの例は以下となる。
import { shallowMount } from '@vue/test-utils';
import { expect } from 'chai';
import targetVue from '@/components/Explanation';
describe('Explanation.vue', () => {
const factory = (propsData)=>{
return shallowMount(targetVue,{
stubs: {
...propsData
}
});
};
describe('画像ファイルの取り扱いへ同意のチェックボックス', ()=>{
it('ダイアログModalConfirmPrivacyでcloseModalOfPrivacy()が呼ばれた後は、id_checkbox_agree_privacyをクリックしてid_agree2policyが表示(チェック)へ変化する', () => {
const wrapper = factory({
ModalConfirmPrivacy : '<div><span id="_stub_id_modal_close" @click="$emit(\'close\')">closeボタンだけを持ったモーダルダイアログへスタブ化</span></div>'
});
// チェックボックス(相当)の初期状態を検証
expect(wrapper.find('#id_agree2policy').isVisible()).to.be.false;
expect(wrapper.find('#id_not_agree2policy').isVisible()).to.be.true;
// ダイアログを開いて閉じる。
wrapper.find('#id_button_confirm_privacy').trigger('click');
wrapper.find('#_stub_id_modal_close').trigger('click'); // ↑をclickしてからでないと、これは取れない。
// チェックボックス(相当)をクリック
wrapper.find('#id_checkbox_agree_privacy').trigger('click');
// チェックボックス(相当)が変更されたことを検証
expect(wrapper.find('#id_agree2policy').isVisible()).to.be.true;
expect(wrapper.find('#id_not_agree2policy').isVisible()).to.be.false;
});
});
});
被テスト対象のVueコンポーネントで $emit() されたことを検証する方法
コンポーネント内で「this.$emit('発火するイベント名称')
が呼ばれたか?」
を検証するには emitted()
を用いる。
具体的なテストコードの例は以下となる。
import { shallowMount } from '@vue/test-utils';
import { expect } from 'chai';
import targetVue from '@/components/Explanation';
describe('Explanation.vue', () => {
const factory = (propsData)=>{
return shallowMount(targetVue,{
stubs: {
...propsData
}
});
};
describe('画像ファイル登録ボタン', ()=>{
it('id_agree2policyが表示(チェック済み)ならば、親コンポーネントのgo2SelectImage()が呼ばれること', () => {
const wrapper = factory({
ModalConfirmPrivacy : '<div><span id="_stub_id_modal_close" @click="$emit(\'close\')">closeボタンだけを持ったモーダルダイアログへスタブ化</span></div>'
});
// ダイアログを開いて閉じる。
wrapper.find('#id_button_confirm_privacy').trigger('click');
wrapper.find('#_stub_id_modal_close').trigger('click'); // ↑をclickしてからでないと、これは取れない。
// チェックボックス(相当)をクリック
wrapper.find('#id_checkbox_agree_privacy').trigger('click');
// 画像登録ボタンをクリック
wrapper.find('#id_button_select_image').trigger('click');
// 親コンポーネントのgo2SelectImage()を呼び出したことを検証
// ※引数がある場合は、キー(例:'go2SelectImage')のバリューとして配列で入ってくる。
// 今回の事例では引数無しなので省略。
expect(wrapper.emitted()).has.property('go2SelectImage');
});
});
});
以上ー。
参考サイト
- イベントをシミュレーションする - Vueテストハンドブック
- wrapperで利用できるプロパティとメソッド
- 一般的なヒント - Vue Test Utils
- trigger(eventType [, options ]) - Vue Test Utils.html
- Vue.jsのテストでコンポーネントをいい感じにwrapする方法
- https://qiita.com/ykhirao/items/8e8a9547a693c677813c
- (※結局私は、↑のQiitaページを見てようやく方法が分かった^^;)