Could we avoid the flake + complexity of $nextTick by bundling a tiny retry helper? #345
Replies: 4 comments 4 replies
-
IMHO I don't think we should package it in VTU. This kind of high level instruction would be more suited to a VTU plugin published a as standalone (as vue-router-mock for example). We probably don't all have the same style of testing, but in my experience it is really important to keep control of the underlying event loop. VTU should offer all the low-level tools, and third-party plugins can add high-level features. Regarding your example, you probably don't want to really wait 500ms (or more!) in a unit test: you're going to use a fake timer to travel to the future and make the test instantaneous :) |
Beta Was this translation helpful? Give feedback.
-
I actually like the idea of including this. One thing someone said that stuck with me is "I can just install test utils and I have everything I need". While you technically do, you need to understand the event loop and the such, something which is not super obvious or accessible to newcomers. One of the reasons testing library is popular is it has a great UX, partly due to things like this. I guess it would both be on |
Beta Was this translation helpful? Give feedback.
-
I'm a bit torn on this one, to be honest 😂:
So, given (1), what if… we suggested people to use Testing Library as a way to hide the complexity of $nextTick and flushPromises? Even Vue 3 docs recommend using VTL over VTU, so we could do the same thing here. FWIW, VTL support for Vue 3 is almost done |
Beta Was this translation helpful? Give feedback.
-
I am currently using https://github.com/sindresorhus/p-wait-for since @testing-library/vue doesn't include the An example: import { mount } from '@vue/test-utils'
import { useQuery, VueQueryPlugin } from 'vue-query'
import { defineComponent } from 'vue'
import { test, expect } from 'vitest'
import waitFor from 'p-wait-for'
test('todos', async () => {
const App = defineComponent({
setup() {
const { data, isSuccess } = useQuery('todo', () =>
fetch('https://jsonplaceholder.typicode.com/todos/1').then(
(res) => res.json
)
)
return {
data,
isSuccess,
}
},
})
const wrapper = mount(App, {
global: {
plugins: [VueQueryPlugin],
},
})
await waitFor(() => wrapper.vm.isSuccess)
await expect(wrapper.vm.data).resolves.toMatchObject({
userId: 1,
id: 1,
title: 'delectus aut autem',
completed: false,
})
}) Would be nice to have a function like |
Beta Was this translation helpful? Give feedback.
-
👋🏻 A while ago I wrote a tiny JS helper to work around some Vue Test Utils flake. It's almost identical to the usage of
waitFor
by the Testing Library. I'd love to bundle this utility with VTU because it gives users the option to retry their assertions through multiple event loop updates.Right now, there's a class of flaky race conditions that depend on when Vue's render loop is updated (and how often). When a component is refactored in a certain way, flake can arise -- even when $nextTick is used correctly. This happens most commonly with components that rely on
watch
internally. Refactors to components should (ideally) never break tests where the DOM contract hasn't changed. Currently, we must instruct users to useawait wrapper.trigger // setValue, setData, etc
in conjunction withflush-promises
(components that use Axios, etc). Using a single retry helper would allow us to remove flush-promises from our documentation and make the nuances of awaiting the event loop less of a focal point in our guides.I wrote about this kind of a retry helper in a blog post back in November (link). Here's the stand-alone Gist of it -- it's actually VTU agnostic, so I'm happy to publish it as a mini standalone library. Regardless, I think it would give our users an API that is resilient to the async nature of reactive components.
I'll inline the vanilla usage here. Ideally, we'd ship this on wrapper (??) or in an exported function from vtu.
Thoughts?
Beta Was this translation helpful? Give feedback.
All reactions