LoginSignup
13

More than 5 years have passed since last update.

@vue/test-utilsで非同期イベントのテスト

Posted at

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を叩いて処理する、みたいなものがあったとします。
サンプルを公式ドキュメントからもってくると以下のような感じです。

App.vue
<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つかってます)

App.spec.js
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が処理されるのでそれをなんらか待つことができればよい、という感じです。

さっきのテストコードは以下になります。

App.spec.js
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-promises1をimportしたのと、itのcallbackをasyncにして、arrow function内でawait flushPromises()している点です。
flushPormisesでいまあるPromiseが全部resolveされるので、それをawaitすると無事非同期処理をはさみつつ、testがかけます。
こっちの方が読みやすいですね。


  1. flush-promisesは別途installする必要があります。yarn add -D flush-promisesなど。 

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13