简体   繁体   中英

How to wait for async mounted of Vue component to finish before continuing with testing

I have a Vue2 component with a async lifecycle hook method:

// create report when component is loading
async mounted(): Promise<void> {
  await this.createReport();
}

Now I would like to test my component via jest and vue/test-utils but the test should await the completion of the component's mounted() method.

const wrapper = await mount(MyComponent, {  // <-- no Promise, await has no effect
  localVue
});

await flushPromises(); // <-- is not waiting for the async mounted()

expect(wrapper.vm.currentReport.sections).toHaveLength(5); // <-- failing since createReport not finished

Unfortunately mount() or shallowMount() do not return a Promise and will not wait for the completion of the lifecycle hook.

So far the only option was await flushPromises() which has no effect on the mounted method. Others wrote await mount() but since no Promise is returned, this has also no effect. I think most tests pass, because they are just fast enough so the mounted is finished already. Which is not the case for me, since we are loading some proper data and that takes a few seconds.

A user will see a loading screen and will wait patiently to interact with the component, jest unfortunately will not wait.

How can I start my Vue tests with jest after the mounted-logic has finished?

If you mark mounted as async you are not preventing the component from mounting until the promise resolved/rejected 1 . It will mount and, at some later point, mounted will resolve/reject.

If you want to wait until something happens (eg: this.createReport() resolves), inside your mounted , after the await , set a boolean to true (eg: this.hasReport ).

Now you can watch that reactive data value and trigger some other functionality or you can use it inside your <template> to prevent some DOM from rendering before it has the necessary data.


The typical way to test async behaviour, is to actually mock the async response. You haven't shown us what createReport() does but it's likely external to the component logic. For unit tests, it doesn't really matter what it does. You should mock all the cases you want to test (resolve success, reject failure, ...).

Why mock? Because, for example, you don't want your tests to fail if the internet is not working when you run the tests. So you shouldn't be making an actual http request to some distant API. You should replace that call with a function (mock), returning a promise resolving with the expected data. Or rejecting with the expected error.

All testing frameworks ( jest , vitest , etc...) make mocking a breeze.

See testing async behavior .


1 - Technically, this statement is only 100% true in Vue 2. Vue 3 has a built-in experimental <Suspense> component, which has two slots: the content and the fallback. If any component, no matter how deep, inside the content slot has asynchronous created and/or mounted hooks, the <Suspense> component will render the fallback slot contents, until all the content promises resolve or reject. When all of them have, it will display the content slot.
This is not relevant for your case, since you're using Vue 2, but I had to specify it to avoid confusion for Vue 3 users.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM