Vueのテストを書いていて、非同期処理の部分でちょっとはまったのでメモ。
結論として、以下の公式ドキュメントを読め、でした。
https://vue-test-utils.vuejs.org/ja/guides/testing-async-components.html
tl;dr
- clickイベント時の処理をtestしたい
- 発火するmethodがasyncだとひと手間必要
-
flush-promises
使うと便利にかける
テストケース
Vue componentでclick(イベントはなんでもいいですが)時に非同期にAPIを叩いて処理する、みたいなものがあったとします。
サンプルを公式ドキュメントからもってくると以下のような感じです。
<template>
<button @click="fetchResults" />
</template>
<script>
import axios from 'axios'
export default {
data () {
return {
value: null
}
},
methods: {
async fetchResults () {
const response = await axios.get('http://example.com/api')
this.value = response.data
}
}
}
</script>
テストファイルは以下のような感じ。(Jestつかってます)
import { shallow } from '@vue/test-utils'
import App from './App'
test('App', () => {
it('fetches async when a button is clicked', () => {
const wrapper = shallow(Foo)
const fetchResults = jest.fn().mockResolveValue({data: 'value'})
wrapper.setMethods({fetchResults})
wrapper.find('button').trigger('click')
expect(wrapper.vm.value).toEqual('value')
expect(fetchResults.calls.length).toEqual(1)
})
})
…がしかしこれはうまくいきません。
fetchResults 内の Promise が resolve する前にアサーションが呼ばれるので失敗します。
解決法
ドキュメントでは$nextTick
を使う方法とasync/awaitを使う方法の2つが紹介されてますが、async/awaitの方が読みやすいのでそっちを使います。
要は、Vue vm内で非同期イベントがtriggerされた場合、vm内のtask queueでPromiseが処理されるのでそれをなんらか待つことができればよい、という感じです。
さっきのテストコードは以下になります。
import { shallow } from '@vue/test-utils'
import flushPromises from 'flush-promises'
import App from './App'
test('App', () => {
it('fetches async when a button is clicked', async () => {
const wrapper = shallow(Foo)
const fetchResults = jest.fn().mockResolveValue({data: 'value'})
wrapper.setMethods({fetchResults})
wrapper.find('button').trigger('click')
await flushPromises()
expect(wrapper.vm.value).toEqual('value')
expect(fetchResults.calls.length).toEqual(1)
})
})
違いは、flush-promises
1をimportしたのと、itのcallbackをasync
にして、arrow function内でawait flushPromises()
している点です。
flushPormises
でいまあるPromiseが全部resolveされるので、それをawait
すると無事非同期処理をはさみつつ、testがかけます。
こっちの方が読みやすいですね。
-
flush-promises
は別途installする必要があります。yarn add -D flush-promises
など。 ↩