Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

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など。 

mohikanz
エンジニアのための雑談コミュニティ
https://mohikanz.slack.com
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away