Is your feature request related to a problem? Please describe.
I am using msw to mock network requests in my unit tests, as per Kent C. Dodds's blog post.
In my test the following actions occur:
I have setup a runtime request handler to handle the second response, however, I want to be able to retrieve the response from the global handler and then patch it in the runtime handler.
Describe the solution you'd like
The library already provides the ability to patch a response however this will bypass any request handlers and call a real API.
What I would like is the ability to call the original request handler inside a runtime handler so that I can get the standard response, allowing me to return a patched response in the runtime handler.
I am not familiar with the inner workings of the library or whether this would be possible but I would expect this to be exposed by a function on the context similar to how we currently have ctx.fetch exposed. The new function could be called ctx.handle to indicate it is calling another handler.
Describe alternatives you've considered
I have considered extracting the response data into an exported module so that I can import it in both handlers, and then modify it in the second handler. This does solve the issue but it seems inelegant, particularly for those who have already written a large suite of global request handlers.
Additional context
Apologies if this already exists as a feature, if it's already possible it would be great to add it to the recipes section of the documentation.
Hey, @tomalexhughes. Thanks for reaching out.
While I'm still processing what you want to achieve, it's worth mentioning that the reason ctx.fetch() exists is to _always bypass any other request handlers_. If you wish for one request handler to hit another use the plain window.fetch() instead.
// Endpoint that always returns the complex data
rest.get('/data', (req, res, ctx) => res(ctx.json(complexDataStructure))),
// Regular handler
rest.get('/endpoint', (req, res, ctx) => {
const data = await fetch('/data').then(res => res.json())
return res(ctx.json(data))
})
// Runtime handler in your tests
worker.use(
rest.get('/endpoint', (req, res, ctx) => {
const data = await fetch('/data').then(res => res.json())
return res(ctx.json({ ...data, slightlyModified: true }))
})
)
Note that while this is a suitable setup, it may be worthwhile to consider isolating the complex data into a JS module and importing it when necessary. There's no overload in doing so, and it may be beneficial to be able to reuse that data in multiple places.
That being said, the regular fetch() will only hit a request handler other than the one it's being dispatch in. The nature of runtime handlers is that they are prepended to the list of all handlers on runtime, and whenever there's a match the matching process ignores any other handlers. In other words, when you add a runtime handler a corresponding regular handler ceases to exist and you cannot hit it in any way, including fetch().
What I'd recommend to do is the following:
// mocks/fixtures/data.json
{ "complex": "data" }
// mocks/handlers.js
import complexData from './fixtures/data.json'
rest.get('/data', (req, res, ctx) => res(ctx.json(complexData)))
// test/your.test.js
import complexData from '../mocks/fixtures/data.json'
server.use(
rest.get('/data', (req, res, ctx) => {
return res(ctx.json({ ...data, slightlyModified: true }))
})
)
Let me know if this satisfies your usage example.
I think that approach works for when the data is static, in another scenario the response is determined by query parameters in the request. For example below, I return an array of the size requested:
rest.get(`/someData `, (req, res, ctx) => {
const months = Number(req.url.searchParams.get("period").split("MONTHS_")[1]);
const startDate = new Date(2020, 0, 10);
const response = [...Array(months)].map((x, i) => ({
startYMD: [
startDate.getFullYear(),
startDate.getMonth() + 1 + i,
startDate.getDay(),
],
value: 50 + i,
}));
});
In this instance I could create a helper function, i.e. instead of having just a fixture I could have getComplexDataFromRequest.
Ideally I was looking to avoid having to extract into a fixtures module and instead rely on composition by calling the original request handler (which will have the core functionality) and then return a modified version of the JSON. This is primarily because I already have a long list of handlers and extracting the functionality of each one into a module would be a lot of overhead, knowing I could call the original handler when overwriting and then modify the response at runtime would be a big time saver in an already established codebase but I recognise this could be difficult if runtime handlers currently overwrite the global.
Essentially being able to do something like the following and skip the fixtures module entirely.
// mocks/handlers.js
rest.get('/data', (req, res, ctx) => res(ctx.json({ "complex": "data" })))
// test file
server.use(
rest.get("/data", (req, res, ctx) => {
const globalHandlerResponse = ctx.handle()
return res(ctx.json({ ...globalHandlerResponse, slightlyModified: true }));
})
);
You can represent dynamic data as functions that generate that data. Then you can reuse those functions in multiple request handlers without having to repeat oneself. There is no difference in terms of maintenance if you have such testing setup.
You can also achieve what you've described (keeping the data in the handler), with the only exception that the base handler (that you want to hit in other handlers) needs to have a _different path_. See my recommended solution from the post above for the code example.
I don't think that adding something like ctx.handle() is a good idea API-wise. It would bring too much complexity and require the library to orchestrate request handler calls outside of the captured request. I'd keep the API simple and thus predictable.
Most helpful comment
You can represent dynamic data as functions that generate that data. Then you can reuse those functions in multiple request handlers without having to repeat oneself. There is no difference in terms of maintenance if you have such testing setup.
You can also achieve what you've described (keeping the data in the handler), with the only exception that the base handler (that you want to hit in other handlers) needs to have a _different path_. See my recommended solution from the post above for the code example.
I don't think that adding something like
ctx.handle()is a good idea API-wise. It would bring too much complexity and require the library to orchestrate request handler calls outside of the captured request. I'd keep the API simple and thus predictable.