-
Notifications
You must be signed in to change notification settings - Fork 667
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add async guide #282
Add async guide #282
Changes from 9 commits
b16c3c2
c9180e1
9e64bcb
0d2959e
b600b8a
1a31d96
309aebc
34c8a38
d675067
7b88e8a
cc8aa0a
46d7265
cc47f9f
85e97e3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
# Testing Asynchronous Behavior | ||
|
||
To simplify testing, `vue-test-utils` applies 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. | ||
|
||
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 is using Jest to run the test and to mock the HTTP library `axios`. | ||
|
||
The implementation of the `axios` mock looks like this: | ||
|
||
``` js | ||
export default { | ||
get: () => new Promise(resolve => { | ||
resolve({ data: 'value' }) | ||
}) | ||
} | ||
``` | ||
|
||
The below component makes an API call when a button is clicked, then assigns the response to `value`. | ||
|
||
``` js | ||
<template> | ||
<button @click="fetchResults" /> | ||
</template> | ||
|
||
<script> | ||
import axios from 'axios' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you add a line break after the import statement |
||
export default { | ||
data () { | ||
return { | ||
value: null | ||
} | ||
}, | ||
|
||
methods: { | ||
async fetchResults () { | ||
const response = await axios.get('mock/service') | ||
this.value = response.data | ||
} | ||
} | ||
} | ||
</script> | ||
``` | ||
A test can be written like this: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add a link to the Jest manual mocks page—https://facebook.github.io/jest/docs/en/manual-mocks.html#content |
||
|
||
``` js | ||
import { shallow } from 'vue-test-utils' | ||
import Foo from './Foo' | ||
jest.mock('axios') | ||
|
||
test('Foo', () => { | ||
it('fetches async when a button is clicked', () => { | ||
const wrapper = shallow(Foo) | ||
wrapper.find('button').trigger('click') | ||
expect(wrapper.vm.value).toEqual('value') | ||
}) | ||
}) | ||
``` | ||
|
||
This test currently fails, because the assertion is called before the promise resolves. One solution is to use the npm package, `flush-promises`. which immediately resolve any unresolved promises. This test is also asynchronous, so like the previous example, we need to let the test runner know to wait before making any assertions. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
to
|
||
|
||
If you are using Jest, there are a few options, such as passing a `done` callback, as shown above. Another is to prepend the test with the ES7 'async' keyword. We can now use the the ES7 `await` keyword with `flushPromises`, to immediately resolve the API call. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It isn't shown above. Can you add an example using test('Foo', (done) => {
it('fetches async when a button is clicked', () => {
const wrapper = shallow(Foo)
wrapper.find('button').trigger('click')
setTimeout(() => {
expect(wrapper.vm.value).toEqual('value')
done()
})
})
}) (note I haven't tested this code) We should add a note on why this works, with a link to this blog post—https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/. Basically, the microtask queue, which processes promise callbacks, runs before the task queue, which processes timeout callbacks. So when a setTimeout callback is executed, any resolved promises will have been executed. |
||
|
||
The updated test looks like this: | ||
|
||
``` js | ||
import { shallow } from 'vue-test-utils' | ||
import flushPromises from 'flush-promises' | ||
import Foo from './Foo' | ||
jest.mock('axios') | ||
|
||
test('Foo', () => { | ||
it('fetches async when a button is clicked', () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You need to make the function async, that's why there's a linting error: to
|
||
const wrapper = shallow(Foo) | ||
wrapper.find('button').trigger('click') | ||
await flushPromises() | ||
expect(wrapper.vm.value).toEqual('value') | ||
}) | ||
}) | ||
``` | ||
|
||
This same technique can be applied to Vuex actions, which return a promise by default. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to