Jest + Axios + flushPromises is not enough to wait for Mirage response
See original GitHub issueThis might not be Mirage related, in that case I’m sorry.
In a Nuxt (Vue) app, I have a component test like this:
wrapper = render(MyForm);
fillIn(wrapper.find('input').element, 'valid@gmail.com');
wrapper.find('button').trigger('submit');
await flushPromises();
expect(onSuccess).toHaveBeenCalled();
That test was fine until I moved from axios
mocking towards using Mirage. It seems like await flushPromises();
is not enough anymore and the expect(onSuccess).toHaveBeenCalled();
gets called too early.
In some scenarios I could introduce different waiting strategy. I could wait for error or success message rendering in the DOM (await waitForElement(() => {}
); But in this case, there’s the prop callback being called and that’s it.
I can see that the route handler is reached before the test ends - even before the assertion (🙏 ) so all I need is a tiny bit of extra waiting, so it would be awesome if I could do something like
await server.idle()
There’s one reliable solution and that’s
await waitFor(() => expect(onRegister).toHaveBeenCalled());
But it seems its significiantly slower (up to extra 50 - 100ms from my measurements, which makes sense, given it loops in 50ms intervals). With the waitFor(() => {})
. This test takes up to 200ms which seems like it’s more than it should. But I could be wrong here and it’s normal:)
Issue Analytics
- State:
- Created 3 years ago
- Comments:8 (3 by maintainers)
Top GitHub Comments
Hello,
I am bumping this issue because I ran into the same problem earlier this year when trying to setup Mirage in a test environment. I found the source of the problem and the solution, which involves no delays in tests 😄.
tl;dr Setting
server.timing
to false (not 0) after the server creation will allow you to simply useawait flushPromises
/await act
/await waitFor
without any delay.Reason
Mirage uses Pretender to handle routes, and includes the timing property.
To introduce that delay, Pretender uses
setTimeout
. This is why awaiting for promises has no effect here.It is also not possible to use fake timers to instantly execute
setTimeout
calls, since for various feature reasons Pretender actually performs 50ms longsetTimeout
calls, and checks the actual elapsed time. It also does that using recursive calls, so attempting to fakesetTimeout
will result in a good old stack overflow.Solution
As a documented feature, the
timing
parameter can also be a boolean. If set to false, it will make the request synchronous. After checking, I can confirm it skips everysetTimeout
calls.Right now Mirage does not support having a false
timing
option when creating a server. Probably part of a bug by the way, as the linethis.timing = this.timing || config.timing || 400
can never result inthis.timing
being set to 0 in a development environment.However it is possible to force it after the server creation, using
server.timing = false
. I suppose this is officially supported as the documentation mentions being able to force a timing for a specific test this way.I can confirm it works wonderfully my test suite in a React environment using testing-library. For instance:
It also works well with
waitFor
orflushPromises
, and will not have any delay.Updating Mirage
I would suggest that instead of forcing
timing
to 0 for tests, it is forced to false instead. I don’t think this would break existing tests, unless some nasty workaround has been done like waiting forsetTimeout
to be called.For safety though, I also suggest either allowing
timing
to be 0 in config, and/or adding a booleanasync
option to be able to have more control over that behavior.I am currently trying the changes on my side and will create a pull request when it is done.
@dv297 To get your application tests working the best approach is to not wait a specific amount of time, but to wait for an element to be in the document. Check out the guides on appearance and disappearance.
This should be enough to get your tests passing.
I think this also has the benefit of making your tests more robust since your no longer relying on implementation details like time or currently resolved promises.