I wrote a bunch of tests on a Vue project using Vitest, and had pretty good success with it. Quite a few tests deal with asynchronous code, such as a service call that returns a promise.
Tick's in Vue
To test this, I would mock the Promise and then force it to resolve an answer you have control over. Something, conceptually, like this:
2import { nextTick } from "vue"
3
4it('Some Test', () => {
5 let myResolve : Function;
6
7 axios.post = vi.fun(() => {
8 return new Promise((resolve) => {
9 myResolve = resolve;
10 })
11 });
12
13 service.myFunctionalThatCallsAxiosPost().then((result) => {
14 // assertions here
15 });
16 myResolve('some result value');
17 nextTick();
18})
This should be considered psuedo-code, but is very similar to what I might write in the real world. First, we mock axios's post() function, and save the Promise's resolve function into a variable.
Then we call a function that will make the axios post, and return the promise, and we listen for the results. The nextTick() function is what forces the promise to actually resolve. This is all under the hood stuff about mock timers, and how testing works with the JavaScript event loop.
Throw some assertions in the result handler, and you're good to go.
Node's process.nextTick()
Fast forward to today, and I'm using Vitest on a TypeScript only project. But, you'll notice that the `nextTick()` in the previous section is actually a Vue import. What is one to do?
Well, I googled it and got a lot of suggestions that I use processTick. first you'll want to Google it. And the Google AI results told me to use process.nextTick.
Modified code like this:
2
3it('Some Test', async () => {
4 let myResolve : Function;
5
6 axios.post = vi.fun(() => {
7 return new Promise((resolve) => {
8 myResolve = resolve;
9 })
10 });
11
12 service.myFunctionalThatCallsAxiosPost().then((result) => {
13 // assertions here
14 });
15 myResolve('some result value');
16 await process.nextTick();
17})
The code block is almost identical, except for the removal of Vue's nextTick() function, being replaced w/ NodeJS nextTick() function.
This worked great when I was writing and testing individual tests, however once I ran the test suite, I had all sorts of issues. It turns out the process.nextTick() function was resolving promises after the test suite ran, not as part of an individual tests. As such, the test would sporadically fail, because different tests would change different values, properties, and cause different behavior inside the class I was testing.
The behavior surprised me a bit, since I tested it with and without await on the test function and async on the tick. It just didn't seem to wait for the actual tick.
This didn't work for me.
Flush Promises
The solution was to use an npm library named Flush-promises.
2import { flushPromises } from "flush-promioses
3
4it('Some Test', async () => {
5 let myResolve : Function;
6
7 axios.post = vi.fun(() => {
8 return new Promise((resolve) => {
9 myResolve = resolve;
10 })
11 });
12
13 service.myFunctionalThatCallsAxiosPost().then((result) => {
14 // assertions here
15 });
16 myResolve('some result value');
17 await flushPromises();
18})
This was the magic sauce I needed. Import the new library, and run it after you resolve your promise. Now the promises resolve as part of the test, before any future tests can change values causing unexpected test behavior, and assertions to fail.
Final Thoughts
Over the past year I've written unit tests in Vitest, Karma/Jasmine, and Jest. I was under the impression that Jest was the preferred method in the industry, however Vitest has really grown on me.