-
-
Notifications
You must be signed in to change notification settings - Fork 533
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Question: request handler request history #339
Comments
Hey, @ilyaulyanov. Thanks for reaching out. I'm afraid accessing such list and performing assertions on it would be implementation details testing. Instead of checking internals of MSW and looking up if some handler was called with some parameters, think of it this way: if a handler is called with invalid parameters, your React component's output will be invalid. Assert what your component renders, not the network layer. Take a look at the similar question that's been raised recently. I go into details and provide usage examples that lean your test towards testing what matters. I can see such API being useful only for development purposes. It brings too much temptation to test if something was called, or was called with the right parameters, which is something MSW actively discourages. Test how your component reacts to the response, emulate unsuccessful and error responses, provide conditional mocked responses based on the request parameters in your response resolvers—this will give you much more confidence in your tests. Hope this helps. |
@kettanaito thanks for an elaborate and quick response. I agree that having the ability to see that would be tempting, and lead to implementation testing and all problems associated with it. Having to write a conditional mocked response seems as the way to go here for me. Even though it has a risk of introducing false positive / negative results due to an incorrect implementation, it's still way more "real" than a mocked API module. I'm currently experimenting with It certainly feels like with If you see conditional responses as a solution to mine & similar issue, maybe it's worth adding it to the Recipes section? I can help if needed. The rest of the runbook, including recipes, was really helpful when setting it up for an existing app. |
@ilyaulyanov consider reusing the condition logic from the rest of your app. Since mocks execute in the same context, you can call the same validation functions in the mocks, for example, that you do in the actual application's implementations. This is a solid way of ensuring you don't get false positive/negative results in your tests.
It may feel like repetition, but in reality it's depicting the behaviors that make your request valid. Not all business logic should be copied to response resolvers. The one that does helps you to reason about API changes over time, acting like a snapshot of an API contract once established. This is one of the arguments against any kind of sync between the actual server and mocks: it would destroy the reproducibility of a mock. Thank you for the suggestion, I think it's a good idea to put these recommendations into a recipe. I've added the Request assertions recipe, please take a look. |
Looks good to me! |
hey @kettanaito - thanks for this explanation here, but I seem to have a caveat / related question for you... How could one go about testing different UI state based on an Apollo query that utilizes a I.e. first request responds with this result, second request delays, then responds...etc... Any ideas? |
Hey, @priley86. Thanks for reaching out. The const GET_USER = gql`
query GetUser {
user: {
firstName
}
}
`
function Example() {
const { data } = useQuery(GET_USER, { pollInterval: 1000 }) // request each second
// handle loading and error
return <p>{data.firstName}</p>
} // mocks.js
import { setupWorker, graphql } from 'msw'
const worker = setupWorker(
graphql.query('GetUser', (req, res, ctx) => res(ctx.data({ user: { firstName: 'John' } }))
)
worker.start() This way your Does this answer your question? Feel free to clarify in case I got it wrong. |
I think this example helps and helps me think differently about polling and testing w/ msw. Initially I was thinking that some kind of initial response could occur, and then a subsequent one after a delay with a different response (say to test the loading and error states above) using the same resolver. The client would be making the same query and just initiating a poll. However another potential way of testing these different loading/error states having the same query vars after a poll is just referencing different msw resolvers in your test. Thoughts? |
i.e., no request history would be needed... |
However, this is not how polling works. Polling is entirely client-side technique that provides data synchronization via repetitive request in a defined time interval. From the server's perspective polling doesn't exist, as the server receives a request and responds to it as usual. In a traditional HTTP communication server cannot send a response without a preceding request first. You may be confusing polling with real-time subscriptions (i.e. WebSocket), which are not yet supported by MSW (see #156).
Sorry, I'm not sure I follow you on this one. Please, could you share some code (pseudo-code is fine) of how you see this working? |
Found this issue by searching for useEffect(() => {
fetch(`/api/todo/${todo.id}`)
}, [todo]) So far the only way I can do think of is spying on |
@NMinhNguyen can you assert the opposite, that only known requests were made? You can use the // Fail the test if it makes a request not listed in the handlers.
server.listen({ onUnhandledRequest: 'error' }) If you wish to scope the list of allowed requests, use the // The server may be created for all tests and may include handlers
// that describe requests you wish not to be made in this particular case.
const server = setupServer(...handlers)
server.listen({ onUnhandledRequest: 'error' })
afterEach(() => {
// Reset handlers after each test to the list of the "handlers"
// initially passed to "setupServer" call.
server.resetHandlers()
})
it('does not make any unknown requests', () => {
// Only the handlers listed below will be treated as known.
server.resetHandlers(rest.get('/api/todo/:id', resolver)
// ...run your code
// ...assert result
}) The suggestion above is applicable only if your initial handlers describe requests that you don't wish to be made in the currently tested component. |
Thanks @kettanaito, is it possible to make tests fail
In my case, I actually don't want any requests to be made (due to some conditional logic). Is there a valid way to call Regarding |
You can use the
That's how I'd expect it to work, but I haven't tried to use it this way.
I don't think we have a suitable API to recommend in this case at the moment. Based on the requirements, what you'd probably need is to capture all requests and make them return 500 responses/throw an error. |
You're right, I just checked the implementation, and I'm pretty sure it works the way you described 🙂
Makes sense, thank you! |
Is your feature request related to a problem? Please describe.
When using
msw
in a test environment, I'd like to be able to retrieve history of requests / responses for a certain handler. Do they get stored anywhere?When using axios, there's a way to do it if you use
axios-mock-adapter
, that provides history per method.Without mocking axios, I can't find a way to retrieve the last request made so I can verify that a request with certain params had been made.
Given
PetsList.test
Additional context
The text was updated successfully, but these errors were encountered: