Vue-test-utils: add section to docs on testing components that include promises

Created on 4 Nov 2017  Â·  9Comments  Â·  Source: vuejs/vue-test-utils

First, I am not sure if this is something vue-test-utils should do or something the test runner should handle. It's just an idea I had to solve a problem I found a lot while using vue-test-utils.

A number of libraries (for example vee validate, which I have been using heavily in my app) rely on promises. In the case of vee validate, errors are rendered async. vue-test-utils does everything sync. Another common test case is asserting some UI changed based on a mock API response, that uses a promise.

Both these cases require the test to wait for a promise to resolve (even if it is one that resolves almost immediately). vue-test-utils is all sync, I have to use the test runner (in this case jest) to await the promise, or the wrapper.vm is not updated.

The below example shows the first case (failing for the reason outlined above) and a workaround.

My original idea was a triggerAsync method:

wrapper.find('input').triggerAsync('click', (done) => {
  // wait for promises to resolve
  // wrapper.update() so computed/data values, and UI is updated
  // assertions
  done()
})

However I am starting to think I am heading down the wrong path.

Example:
Component

<template>
  <div id="app">
    {{ msg }}
    <button @click="handle">Button</button>
</template>

<script>
export default {
  name: 'app',

  data () {
    return {
      msg: '',
    }
  },

  methods: {
    promise () {
      return new Promise(res => {
        res()
      })
    },

    handle () {
      this.promise()
        .then(() => {
          this.msg = 'testing'
        })
    }
  }
}
</script>

Test:

import { shallow } from 'vue-test-utils'
import App from './App'

describe('App', () => {
  // fails because async promise
  it('sets some text', () => {
    const wrapper = shallow(App)

    wrapper.find('button').trigger('click')

    expect(wrapper.vm.msg).toBe('testing')
  })

// returning a promise in jest lets this pass
it('sets some text', () => {
    const wrapper = shallow(App)

    return new Promise(res => {
      wrapper.find('button').trigger('click')
      res()
    })
    .then(() => {
       expect(wrapper.vm.msg).toBe('testing')
    })
  })
})

https://github.com/lmiller1990/vue-test-utils-async for example using vee-vadidate

Or, am I venturing out of the realm of unit test into e2e test here?

discussion docs intend to implement

Most helpful comment

There should definitely be a section in the docs, especially since plugins like vee-validate, which uses Promises quite heavily, are popular.

All 9 comments

We could add an updateAsync method that can be called with await:

wrapper.trigger('click')

await wrapper.updateAsync()

// wrapper updated

I've been using a library called flush-promises. You can write nice tests in Jest with async/ await that stops you adding setTimeouts inside the tests.

npm install --save-dev flush-promises
import flushPromises from 'flush-promises'

it('sets some text', async () => {
    const wrapper = shallow(App)
    await flushPromises()
    wrapper.find('button').trigger('click')
    await flushPromises()
    expect(wrapper.vm.msg).toBe('testing')
  })

You can see an example here — https://github.com/eddyerburgh/vue-hackernews-chapter-6/blob/master/src/store/__tests__/actions.spec.js#L28

Flush promises looks like it might do nicely. Will try integrate it and see if it solves my problem, and report back.

Have you felt the need or are you happy with just using flushPromises?

Testing the vee-validate example, flush-promises does not work.

EDIT: It appears to be working now after fixing an issue with babel. It would definitely be nicer to have a built in method for handling Promises though.

I don't think vue-test-utils should have a built in way to deal with promises. It's not vue specific, and there are already libraries that flush promises. It's up for discussion, but my opinion is we should use other libraries for it.

Instead we could add a section to the docs.

There should definitely be a section in the docs, especially since plugins like vee-validate, which uses Promises quite heavily, are popular.

@lmiller1990 I changed the title to add a section to the docs, if your original issue wasn't solved we can revert the name.

Slightly off-topic seeing what was discussed above, but since this is now a docs issue on this subject, I'll comment here.

The docs imply (https://vue-test-utils.vuejs.org/en/guides/getting-started.html#what-about-nexttick) that we don't have to worry about component refreshes, but it seems that things like v-if'ed tags still require the use of something like await flushPromises() for any changes to appear. (even if no promises were actively used)

Example:

    <p v-if="feedback">Your question has been updated.</p>
...
        data () {
            return {
                feedback: false
...
    it ('shows feedback conditionally', async () => {
        expect(wrapper.contains('p')).toBeFalsy()

        wrapper.vm.feedback = true
        await flushPromises()

        expect(wrapper.contains('p')).toBeTruthy()
    });

You need to use wrapper.update to update the component synchronously. You should use setData/ setState to update vm data, since they run update for you.

Useful pointers, thanks @eddyerburgh

Was this page helpful?
0 / 5 - 0 ratings

Related issues

alexanderstudte picture alexanderstudte  Â·  3Comments

jonyoder picture jonyoder  Â·  3Comments

vilarinholeo picture vilarinholeo  Â·  3Comments

eddyerburgh picture eddyerburgh  Â·  4Comments

AustinGil picture AustinGil  Â·  3Comments