Integration testing with MSW dynamic mocks
MSW and cypress/playwright are awesome testing tools. However, things can get tricky when you combine them. The crux of the problem is that your test code is not in the same process that your app code is. They are two discreet processes so you don't have direct access to the msw server
instance.
With msw, you typically define the "happy path" responses in a mocks/handlers.ts file and this is used in your mock server. But...what if you want to test an error scenario?
You have a few options.
Dynamic mock scenarios
Basically split up the handlers.ts
into a scenarios
object and add a query parameter to "activate" a certain scenario. You can see this in the msw docs here.
There are two less-than-desirable traits to this approach though:
- They're separate from the test code
- You have to come up with your own arbitrary scenario naming convention
Use a development branch of msw
There is work being done to bridge the gap between msw server and e2e test code. It's just not ready for prime time yet. The setupRemoteServer
API allows you to communicate to your server
over a web socket connection. The idea is very promising and I'm excited for it to get merged! You can help by funding the maintainer's development efforts.
I'd love to ship the remote server API but, sadly, have no time for it right now. If your team would benefit from it, consider funding the effort! You can DM me for more details. Thanks.
— Artem Zakharchenko (@kettanaito) August 27, 2024
Development only app endpoint
I came up with a low-tech setupRemoteServer
alternative to using network behavior overrides in a dedicated test endpoint. I'm using Remix so I simply created a development-only action route that I can make requests to that will interact with the msw server
for me.
Now, I can set up a scenario directly from the e2e test.
The loader
of my /app/contacts
route makes an api call to fetch data from a http://localhost:6000/people
api. The above code allows me to mock that request directly in my test code, keeping it all together.
I can further clean this up by abstracting this into a mock() function.
Hope this helps!
Categories: Remix