この記事について
vue-test-utilsのbetaがそろそろnpmに出るかも1というステータスだったので、
EDIT: betaが公開されました。
公開しているboilerplateでvue-test-utilsを使用してユニットテストを書いてみました。
vue-router、vuex周りのテストの話やavoriazから乗り換える際の変更点を紹介します。
vue-test-utilsとは
Vue.js のテストをサポートするライブラリです。
リポジトリ
ドキュメント
背景としては、
- avoriaz爆誕
- Vue.js公式でもテストをサポートするやつを出した方が良いのでは
- avoriazの作者が中心となってvue-test-utilsを作成開始
といった流れになっています。
avoriazの作者が中心となっているので、APIは多少違うものの似通っています。
そのため、APIについては以下の記事が参考になると思います。
Vue.js用テストライブラリ「avoriaz」入門
ライブラリ
以下のライブラリを使用しています。
ライブラリ | バージョン |
---|---|
vue-test-utils | fe111d7(執筆時点での最新コミットハッシュ) |
karma | 1.7.1 |
mocha | 3.5.3 |
power-assert | 1.4.4 |
sinon | 3.3.0 |
変更点
実際に自分が乗り換えた際の変更点は、このコミットを参考にしてください。
もちろんライブラリ全体としてはまだまだ差異はあると思いますが、見ている感じだと置換でなんとかなりそうなレベルだなーという印象です。
今すぐテストを書かなきゃいけなくのであれば、もう使い始めてもいい段階だと思います。
テスト
ここからはvue-test-utils、avoriazに共通するテスト自体の話です。
mountedフックでこける
テストは基本的に
- mount
- めんどくさいメソッドをスタブ化
- テスト
といった流れになっています。
そのため、mountした時点でスタブ化するヒマなく、Vueのmountedフックが走ります。
APIと通信して初期レンダリングしてるようなやつは、そこでテストがこけてしまいます。
なので、
import MyPage from 'src/components/MyPage'
describe('MyPage', function () {
//mountedフックを削除
delete MyPage.mounted
describe('fetchFollowers()', function () {
it('renders followers when succeed', function () {
...省略
})
})
こんな感じで消しちゃいましょう。
※mountedフックで呼び出しているメソッドのテストは別途行う前提です。
vue-routerのテスト
vue-router自体をスタブ化するのであれば、$router
というオブジェクトに必要なファンクションをスタブ化した状態で作成し、インジェクションしてあげれば良いです。
import { mount } from 'vue-test-utils'
import assert from 'assert'
import sinon from 'sinon'
import Top from 'src/components/Top'
describe('Top', function () {
// スタブ化
const $router = { push: () => { return sinon.stub() } }
...省略
})
ルートコンポーネントでは、router-view
、router-link
のコンポーネントを登録する必要があります。
※vue-test-utils(avoriaz)のshallow
はすでに登録されている子コポンネートをスタブ化してくれるだけで、登録は別途必要となります。
こんなのを用意してあげると、
exports.stubComponent = {
create: (name) => { return { name, render: h => h('div') } },
}
こんな感じで書けます
import { mount } from 'vue-test-utils'
import assert from 'assert'
import App from 'src/App'
import { stubComponent } from 'tests/unit/stubs/component'
describe('App', function () {
const routerView = stubComponent.create('router-view')
const routerLink = stubComponent.create('router-link')
// コンポーネントを登録
Vue.component(routerView.name, routerView)
Vue.component(routerLink.name, routerLink)
...省略
})
Vuexのテスト
Vuexを利用したテストを書くときも流れは変わりません。
スタブ化した後にnew Vuex.Store
してインジェクションという流れです。
Vuex自体のテストを省く場合は、vue-router
と同様、$store
オブジェクトをインジェクションすれば良いです。
(以下のテストではvue-router
のbeforeRouteEnter
が絡むテストになっているので、ややわかりにくいかもしれません...)
import { mount } from 'vue-test-utils'
import assert from 'assert'
import sinon from 'sinon'
import Vuex from 'vuex'
import Auth from 'src/components/Auth'
import AuthModule from 'src/store/modules/Auth'
Vue.use(Vuex)
describe('Auth', function () {
let wrapper = {}
let isAuthorized = false
// スタブ化
AuthModule.actions.verifyToken = () => { return sinon.stub().resolves(isAuthorized)() }
const $router = { push: () => { return sinon.stub() } }
const store = new Vuex.Store({ state: { authUrl: '' }, modules: { Auth: AuthModule } })
// vue-routerのnextをスタブ化
function next(cb) {
if (!cb) return false
cb(wrapper.vm)
}
describe('beforeRouteEnter()', function () {
it('stores token when the url contains hash', async function () {
wrapper = mount(Auth, { store, intercept: { $router } })
const token = await wrapper.vm.$options.beforeRouteEnter({ hash: '#test' }, '', next)
assert(token === 'test')
assert(wrapper.vm.$store.state.Auth.token === 'test')
})
})
})
参考リンク:Vuex自体をスタブ化する場合
参考リンク:Vuexのアクションなどをスタブ化する場合
以上となります、参考になれば幸いです
余談
vue-test-utilsのリポジトリが作成された時はコンテンツは一切なく、
Design / Roadmapというissueが1つポツンとあるだけでした。
ここにEvan You(Vue.js 作者)が人々を招集して、
「さあどういう方針で作ろうか、みんなの知恵を貸してくれ」と話し始めたのはなかなかロックな体験でした。
このライブラリがどういう思想で出来たのかを"使う側"がissueを見て知ることができるのって貴重だと思うんですよねー。