-
Notifications
You must be signed in to change notification settings - Fork 669
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
Conversation
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.
Thanks for the PR! So I wasn't aware that watchers weren't running during update
!
I'm going to run watchers as part of update
, so your original example will pass.
We should keep the section on testing async APIs.
it looks like a good guide. Some minor points:
- can you stylize methods without the parentheses, so
done()
becomesdone
. - we should mention
update
wrapper.find('input').trigger('input') | ||
|
||
wrapper.vm.$nextTick(() => { | ||
expect(wrapper.vm.showButton).toBe(true) |
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.
If this assertion throws, will the error be caught, or will the test time out? In my experience, it's the latter.
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.
Mind you, I've been using mocha which doesn't handle uncaught promise errors. might be different with jest
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.
I think it will time out @callumacrae . What is the best thing to do in this situation?
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.
not sure. i made an issue about it here: #213
@@ -0,0 +1,159 @@ | |||
# Testing Asynchronous Components |
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.
This headline might be confused with "Async components".
Maybe: "Testing Asynchronous Behaviour" ?
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.
Good catch @LinusBorg . I think your suggested title is better. Minor, but In my day to day life I spell it behaviour, should use use American English (no u) or English/Europe/everywhere else English?
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.
Hm. I think we should probably stick do American english as that's the bigger "market"?
OTOH @eddyerburgh is british so ..., :-P
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.
Indeed I am British 🇬🇧
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.
I am Australian (English colony) so we use the same style. But I agree we should use the American English since I believe the official docs would use that.
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.
Yeah, we should use US English
@eddyerburgh good idea about mentioning |
@lmiller1990 At the moment, it forces a component re-render. I think we should also make it run all the component and child component watchers. I'm trying to think whether this would have unintended consequences 🤔 |
Removed Will look add adding more about |
In fact, calling a synchronous method that updates state will also require calling If state is changed outside of vue-test-utils, by mutating the state directly, or by triggering a method, @lmiller1990 An example for this guide would be when you trigger an async method by calling it on the const Component = {
template: `<div>{{someData}}</div>`,
data() {
return {
someData: 0
}
},
methods: {
asyncMethod() {
Promise.resolve(() => {
this.someData++
})
}
}
}
const wrapper = mount(Component)
wrapper.vm.asyncMethod()
await flushPromises()
wrapper.update()
expect(wrapper.text()).toBe(1) |
@eddyerburgh I haven't actually ran into this issue - calling the methods on the wrapper seems to actually implicitly call <template>
<div>
{{someData}}
</div>
</template>
<script>
export default {
data () {
return {
someData: 0
}
},
methods: {
asyncMethod () {
Promise.resolve(() => {
}).then(() => {
this.someData += 1
})
}
}
}
</script> import {shallow} from 'vue-test-utils'
import Update from './Update'
import flushPromises from 'flush-promises'
describe('Update', () => {
it('works', async () => {
const wrapper = shallow(Update)
wrapper.vm.asyncMethod()
await flushPromises()
expect(wrapper.vm.someData).toBe(1)
expect(wrapper.text()).toBe('1')
})
}) I haven't actually hit any situations where I've had to call |
Ah—when flush-promises has finished, the component will have re rendered. Looks like we don't need to include |
@eddyerburgh ok. Is there anything else you think I should change or improve? Would be nice to get this in. Also, I fixed up the failing tests regarding |
Just a thought:. |
</template> | ||
|
||
<script> | ||
import axios from 'axios' |
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.
Could you add a line break after the import statement
} | ||
</script> | ||
``` | ||
A test can be written like this: |
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.
Can you add a link to the Jest manual mocks page—https://facebook.github.io/jest/docs/en/manual-mocks.html#content
}) | ||
``` | ||
|
||
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 comment
The reason will be displayed to describe this comment to others. Learn more.
which immediately resolve any unresolved promises
to
which can be used to flush all pending resolved promise handlers
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 comment
The 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:
it('fetches async when a button is clicked', () => {
to
it('fetches async when a button is clicked', async () => {
|
||
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. | ||
|
||
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 comment
The reason will be displayed to describe this comment to others. Learn more.
such as passing a
done
callback, as shown above
It isn't shown above. Can you add an example using done
. Instead of using flushPromises you can just use a setTimeout:
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.
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.
Looks good, there's just a couple issues to resolve.
Can you test that the examples work, just so we're sure.
Thanks for the feedback @eddyerburgh . Updates:
PS merry christmas! |
}) | ||
``` | ||
|
||
The reason `$nextTick` or `setTimeout` allow the test to pass is because the microtask queue where promise callbacks are processed run efore 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. |
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.
small typo — efore
|
||
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. | ||
Another solution is to use the npm package, `flush-promises`, which can be used to flush all pending resolved promise handlers, and prepend the test with the ES7 'async' keyword. The ES7 `await` keyword with `flushPromises` can now be used to immediately resolve the API call. |
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.
Can we reword this:
Another solution is to use the npm package,
flush-promises
, which can be used to flush all pending resolved promise handlers, and prepend the test with the ES7 'async' keyword. The ES7await
keyword withflushPromises
can now be used to immediately resolve the API call.
To something like this:
Another solution is to use an
async
function and the npm packageflush-promises
.flush-promises
flushes all pending resolved promise handlers. You canawait
the call offlushPromises
to flush the promises and improve the readability of your test.
Done! Thanks for the feedback. |
@lmiller1990 Would u add an example for catching exceptions: |
@doun the current test shouldn't swallow any exceptions, but throw an error. I haven't experienced what you described, can you link to an example/post some sample code? Or do you mean I should add an example showing how to test if an error was throw from some async code? I think a small note might be worth including. |
It's not about current sample code. test("expect fail", async ()=>{
try{
var resultA = await TaskA()
expect(resultA).toBe(true)
var resultB = await TaskB()
expect(true).toBe(false)
}
//all exceptions caught, the test will pass
catch(e){
}
}) I wanted to catch only async exceptions, but the test framework(jest) also use exceptions to catch expect failures. |
@doun Thanks for the sample. It appears you are just testing regular JavaScript methods, not in the context of a Vue component? Or can you provide the component and some more context? What is I'm not sure this belongs here -- of course it is important, but it doesn't feel specific enough to Vue components to include in this particular guide. One good practise can be to use |
No, it’s about async test, not about vue component test. Your idea is awesome, thanks!
… 在 2017年12月25日,下午11:48,Lachlan ***@***.***> 写道:
@doun <https://github.com/doun> Thanks for the sample. It appears you are just testing regular JavaScript methods, not Vue component specific ones? Or can you provide the component and some more context?
I'm not sure this belongs here -- of course it is important, but it doesn't feel specific enough to Vue components to include.
One good practise can be to use expect.assertions at the top of your tests - that way, if an error occurs and Jest skips to the catch block, because expect.assertions is not the correct amount, the test will fail.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub <#282 (comment)>, or mute the thread <https://github.com/notifications/unsubscribe-auth/AAieRGPvkXmG187Ex2t8QP8xH5qvLCIHks5tD8PHgaJpZM4RIB7x>.
|
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.
Two final edits I'd like you to make, then we can merge 🙂
}) | ||
``` | ||
|
||
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 Karma both use `done`. We can use `done` in combination with `$nextTick` or `setTimeout` to ensure any promises resolve before the assertion is made. |
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.
Jest and Karma both use
should be
Jest and Mocha both use
@@ -0,0 +1,98 @@ | |||
# 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. |
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.
applies updates synchronously
to
applies DOM updates synchronously
Thanks , I added those changes. |
Thanks @lmiller1990 🙂 |
Thank you @eddyerburgh , the part about how |
@guokangf can you post your question on stackoverflow with Thanks. |
As discussed in #246 , some more guides for common pitfalls might be nice.
@eddyerburgh this is just my first draft, I'd like some input on the content, style, etc. Thanks!