始めに
最近vue-resource・vue-routerを使ったSPAウェブサービスを開発・運用しており、ふとそろそろUnit Testを書いてみたいな〜と思い、ググってみたのですが思っていた以上に情報が見つからず・・・。ネット上で見つけたのは下記の3つのみ。
公式サイトを見ながら見よう見まねで書いてみたが、ハマりにハマってしまいもう無理だーとVue.jsコミュニティの方々に助けを求めました。皆さんから多数の提案を頂き、無事に解決しました。本当にありがとうございます!
今回はVue.jsコミュニティの方々に教えて頂いた、Vue.js componentのUnitTestを書く方法をまとめます。
前提
下記ライブラリを使用します。
- Vue.js 2.0 + vue-resource + vue-router + vueify
- karma
- mocha
- chai
テストケース 3パターン
- 普通のコンポーネントメソッド
- vue-routerのパラメーターを利用したメソッドのテスト
- vue-resourceでAPI呼び出しをしているメソッドのテスト
テストについて
- Vue.jsの
- .vueのコンポーネントを対象とし、methods内のメソッドをテストします。
- 一部省略していますが、
こちらにサンプルコードが置いてありますのでこちらをご覧ください。
パターン1:普通のコンポーネントメソッド
まずは外部ライブラリ、リソースを全く利用しない一番シンプルな例です。下記のような addという足し算メソッドがあったとします。
export default {
methods: {
add(a, b) {
return a + b;
}
}
}上記のメソッドのテストをする場合は以下のように記述します。ポイントは以下の通りです。
- テスト対象のコンポーネントをimportします。(読み込み時にファイルパスは適度の調整してください。)
- コンポーネント内のメソッドへはmethodsを通じて呼び出すことができます。これはVueの基本仕様通りメソッドはmethods内にあるからです。
import Test from '../../../src/js/components/test.vue';
describe('Testコンポーネント', () => {
it('足し算', () => {
expect(Test.methods.add(1, 2)).to.be.eql(3);
})
});また下記のようにすればメソッドの存在確認テストもできます。
import Test from '../../../src/js/components/test.vue';
describe('Testコンポーネント', () => {
it('足し算', () => {
expect(Test.methods.add).to.be.a('function');
})
});ここまでは自分で出来ました。
パターン2:vue-routerのパラメーターを利用したメソッドのテスト
ここからは自分では解決できず、コミュニティの方の助言で解決しました。例えば下記のように vue-router のパラメーターを取得して文字列結合をするとします。
export default {
methods: {
myName() {
return this.$route.params.name + ' Taro';
}
}
}上記のメソッドのテストをする場合は以下のように記述します。ポイントは以下の通りです。
- Vue.extendでサブクラスを作成し、テストケースの中でインスタンス化します
- またインスタンス化時に
beforeCreateでルーティングのパラメーターを設定することが出来、ルーティングされた体でテストを書くことが出来るようになります。
import Vue from 'vue';
import _Test from '../../../src/js/components/test.vue';
const Test = Vue.extend(_Test);
describe('Testコンポーネント', () => {
it('vue-routerを利用した場合のテストケース', function() {
const vm = new Test({
beforeCreate() {
this.$route = { params: { name: 'Yamada' } }
}
});
expect(vm.myName()).to.be.eql('Yamada Taro')
});
});パターン3:vue-resourceでAPI呼び出しをしているメソッドのテスト
今回一番悩んだのがこのテストケースでした。APIを利用してJSONで値を取得し、success時にデータオブジェクトのprofileを変更したい場合です。API呼び出しには vue-resourceのグローバル関数 Vue.http.get を利用していました。メソッドの返り値はPromiseとなり、非同期処理のテストってどうやるのか・・・。
export default {
data() {
return {
profile: {}
}
},
methods: {
fetchProfile() {
Vue.http.get('http://localhost:8000/api/profile').then((response) => {
console.log("success");
this.profile = response.json();
}, (response) => {
console.log("failure");
});
}
}
}ゴールのイメージとしては外部リソースであるAPIを実行しないようにモック化し、テスト対象のメソッド内で置き換えをすることです。
今回は Vue.http.getとグローバル関数を呼ぶので、下記のように 算出プロパティを利用して $Vueからテスト対象オブジェク自身を取得できるように調整します。
export default {
data() {
return {
profile: {}
}
},
computed: {
$Vue() { return Vue; }
},
methods: {
fetchProfile() {
Vue.http.get('http://localhost:8000/api/profile').then((response) => {
console.log("success");
this.profile = response.json();
}, (response) => {
console.log("failure");
});
}
}
}次にテストケースです。ポイントは以下の通りです。
- まずテストケースの引数にdoneを指定します。
mochaの仕様でdone()の実行をもって非同期テストが終了したことをmochaに通知します。(通例的に doneと命名されるそうです)
- Vue.http.getでAPIを実際に呼び出さないようにモック化。getメソッドでPromiseの成功時にjson()メソッドがprofileのオブジェクトを返すように設定します。sinon等のモックライブラリを使わず、今回は仮の値を返す方式でも問題ありません。そしてテストケース2と同じ方式でコンポーネントをインスタンス化し、今回のテストケース用に設定した
$Vueを通じて、Vueインスタンスのグローバル変数を上書きします。
- 最後にテスト対象のメソッドを実行します。今回は非同期のテストにつき、そのままではPromiseの成功処理に入らないので、setTimeoutで擬似的に非同期にします。setTimeout内のdone()の実行をもって非同期テストが終了したことをmochaに通知され、モックで指定した通り
Promise.resolveで成功処理に入りテストケースが通ります。
import Vue from 'vue';
import _Test from '../../../src/js/components/test.vue';
const Test = Vue.extend(_Test);
describe('Testコンポーネント', () => {
// ステップ1:引数のdoneを指定
it('vue-resourceを利用した場合', (done) => {
let profile = { nickname : 'hoge' };
// ステップ2:Vue.http.getをモック化
const vm = new Test()
vm.$Vue.http = {
get () {
return Promise.resolve({
json () { return profile }
});
}
}
// ステップ3:setTimeoutで非同期呼び出し
vm.fetchProfile();
setTimeout(() => {
expect(vm.profile).to.be.eql(profile)
// mochaの非同期用テスト仕様で、done()が実行されるまでテストは実行されない。
done();
}, 0)
});
});Special Thanks
今回はVue.jsコミュニティの下記の方々に1週間に渡って何度も助言頂きました。最後にはパッチまで準備して頂きありがとうございます!とても勉強になりました。
感想
普段からJavaScriptのテストコードを全く書いたことがなかったので、正直基本的な事から理解が出来ず苦労しました。ただVue.jsを通じてテストを書く機会が生まれて、この3パターンなら書けるようになりました。JavaScriptのテストコードコード楽しいですね〜。もっと書きたくなったぞー!