Skip to content

Commit 2819e51

Browse files
lmiller1990eddyerburgh
authored andcommitted
docs: add async guide (#282)
1 parent 3a11810 commit 2819e51

File tree

1 file changed

+98
-0
lines changed

1 file changed

+98
-0
lines changed
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# Testing Asynchronous Behavior
2+
3+
To simplify testing, `vue-test-utils` applies DOM updates _synchronously_. However, there are some techniques you need to be aware of when testing a component with asynchronous behavior such as callbacks or promises.
4+
5+
One of the most common asynchronous behaviors is API calls and Vuex actions. The following examples shows how to test a method that makes an API call. This example uses Jest to run the test and to mock the HTTP library `axios`. More about Jest manual mocks can be found [here](https://facebook.github.io/jest/docs/en/manual-mocks.html#content).
6+
7+
The implementation of the `axios` mock looks like this:
8+
9+
``` js
10+
export default {
11+
get: () => new Promise(resolve => {
12+
resolve({ data: 'value' })
13+
})
14+
}
15+
```
16+
17+
The below component makes an API call when a button is clicked, then assigns the response to `value`.
18+
19+
``` html
20+
<template>
21+
<button @click="fetchResults" />
22+
</template>
23+
24+
<script>
25+
import axios from 'axios'
26+
27+
export default {
28+
data () {
29+
return {
30+
value: null
31+
}
32+
},
33+
34+
methods: {
35+
async fetchResults () {
36+
const response = await axios.get('mock/service')
37+
this.value = response.data
38+
}
39+
}
40+
}
41+
</script>
42+
```
43+
44+
A test can be written like this:
45+
46+
``` js
47+
import { shallow } from 'vue-test-utils'
48+
import Foo from './Foo'
49+
jest.mock('axios')
50+
51+
test('Foo', () => {
52+
it('fetches async when a button is clicked', () => {
53+
const wrapper = shallow(Foo)
54+
wrapper.find('button').trigger('click')
55+
expect(wrapper.vm.value).toEqual('value')
56+
})
57+
})
58+
```
59+
60+
This test currently fails because the assertion is called before the promise in `fetchResults` resolves. Most unit test libraries provide a callback to let the runner know when the test is complete. Jest and Mocha both use `done`. We can use `done` in combination with `$nextTick` or `setTimeout` to ensure any promises resolve before the assertion is made.
61+
62+
``` js
63+
test('Foo', () => {
64+
it('fetches async when a button is clicked', (done) => {
65+
const wrapper = shallow(Foo)
66+
wrapper.find('button').trigger('click')
67+
wrapper.vm.$nextTick(() => {
68+
expect(wrapper.vm.value).toEqual('value')
69+
done()
70+
})
71+
})
72+
})
73+
```
74+
75+
The reason `$nextTick` or `setTimeout` allow the test to pass is because the microtask queue where promise callbacks are processed run before the task queue, where `$nextTick` and `setTimeout` are processed. This means by the time the `$nexTick` and `setTimeout` run, any promise callbacks on the microtask queue will have been executed. See [here](https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/) for a more detailed explanation.
76+
77+
Another solution is to use an `async` function and the npm package `flush-promises`. `flush-promises` flushes all pending resolved promise handlers. You can `await` the call of `flushPromises` to flush pending promises and improve the readability of your test.
78+
79+
The updated test looks like this:
80+
81+
``` js
82+
import { shallow } from 'vue-test-utils'
83+
import flushPromises from 'flush-promises'
84+
import Foo from './Foo'
85+
jest.mock('axios')
86+
87+
test('Foo', () => {
88+
it('fetches async when a button is clicked', async () => {
89+
const wrapper = shallow(Foo)
90+
wrapper.find('button').trigger('click')
91+
await flushPromises()
92+
expect(wrapper.vm.value).toEqual('value')
93+
})
94+
})
95+
```
96+
97+
This same technique can be applied to Vuex actions, which return a promise by default.
98+

0 commit comments

Comments
 (0)