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
cy.route() unable to mock same url multiple times if requests happen quickly #4460
Comments
I can recreate this behavior in Cypress 3.4.0 with the tests here. In the example provided, the application does a request every 200ms, and if you want the response to change for each response - like id: 1, id: 2, id: 3, it'll always respond with the first cy.route() response definition, even though you want it to respond with the 2nd at that point. It's almost as if we want to have a |
Same. Need a way to specify the response of the Nth request. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
I came up with a really odd way thats solved a similar problem but not quite the same. In the context of cypress we append an export function createCypressUrlOrDefault(url, intent) {
if (!url) {
throw Error('Url must be supplied!');
}
if (!intent) {
throw Error('A value must be supplied for intent for cypress stubbing!');
}
if (root.Cypress) {
return `${url}?${intent}`;
}
return url;
} |
@bautistaaa @avilaj I considered that too but didn't really want to have the client do different things in test mode. Another option would be to add an artificial delay in the client code (between the requests). Again though that requires special code to run in the client for test mode only which is less than ideal. |
@jennifer-shehane any followup on this? Mocking GraphQL calls is near impossible without this. |
@lifeiscontent
|
@lifeiscontent if you can add a dummy |
@aaron-peloquin @btferus5 sure, but that's clearly a hack, I'd expect cypress to have support for this, not having to hack the library, then later once they've figured it out, undo all the hacks. |
@lifeiscontent no need to hack the library, it's a built in option using context. It also gives you better visibility in and outside of Cypress just debugging issues and tracking network requests. I also don't see how cypress would implement an alternative way to track requests that all share the same URL |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
I have the same kind of question. it('calls the route twice', () => {
// Assign: stub first request
cy.route('POST', `**/employees/options`, 'fixture:employees/options/post-200-unfiltered.json').as('firstRequest')
// Act
cy.openDetail(customerId)
// Assign: Override the route to now only return the filtered options
cy.wait('@firstRequest').then(() => {
// This second request has some filters in the request body
cy.route('POST', `**/employees/options`, 'fixture:employees/options/post-200-filtered-byid.json').as('secondRequest')
})
// Assert
cy.wait('@secondRequest')
}) In this case second request is never asserted. Also without the |
Should be possible to discrimate request with body's content? |
Apollo/GraphQL user here too But in the documentation it's actually make new requests, it's not making multiple XHR calls on page load. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Jup. Also have this issue. Have a polling service I'm trying to test. Want it to poll a few times, then change the response, and then it should trigger a call to a new endpoint. Doesn't matter what I do, it just returns the original response. |
I've run into the same issue and have created a command that supports 2 different methods which should be a good jumping off point for those that need it.
|
I want to make multiple requests to the same endpoint but having different response:
But I got a timeout on waiting for the first request. How do I fix this? |
I used the suggestions in this thread to write a blog post with something I hope is reusable across projects. Please take a look: https://medium.com/life-at-paperless/cypress-graphql-response-mocking-7d49517f7754 |
U saved my life!! It worked for me too |
Unfortunately this pattern does not seem to work with |
You could always revert back to version 5 where server/route is still available |
Yes that's exactly what I'm doing. Server/route are still available on v6 |
I was having this issue as well, and came up with a not so terrible workaround without reverting to cy.server or cy.route. var i = 0
cy.intercept('GET', url, (req) => {
if (i === 0) {
req.reply({
statusCode: 200,
fixture: 'firstResponse.json'
})
} else {
req.reply({
statusCode: 200,
fixture: 'secondResponse.json'
})
}
i++
}); |
I would like to see the resolution of this issue #9302, so that afterwards hopefully this can be resolved by using the new |
|
This has just become a problem for us. We were advised to start writing tests like this: https://docs.cypress.io/guides/references/best-practices.html#Having-tests-rely-on-the-state-of-previous-tests This means that separate However, I'm not able to intercept the same endpoint more than once in a single |
@craig-dae in your test you do something like this? cy.intercept('POST', '/somewhere').as('posted')
cy.click()
cy.wait('@posted') // 1
cy.click()
cy.wait('@posted') // 2
cy.click()
cy.wait('@posted') // 3
cy.click()
cy.wait('@posted') // 4 |
Similar.
Where
The problem is that only the first We use Cypress as regression tests, and we catch a LOT of backend problems that they don't catch with their unit tests. Something as simple as verifying that we got a 200 or 201 catches a LOT of bugs. |
@craig-dae I hear you, just confirming the response is a good strategy. So I have tried to recreate your problem in our cypress-example-recipes example https://github.com/cypress-io/cypress-example-recipes/tree/master/examples/stubbing-spying__intercept
What happens if you do not use a custom command and instead have the test like I have written it? it('spies on multiple requests', () => {
cy.intercept({
method: 'POST',
pathname: '/users',
}).as('postUser')
cy.get('#post-user').click()
cy.wait('@postUser').its('response.statusCode').should('equal', 201)
// post 2nd time
cy.get('#post-user').click()
cy.wait('@postUser').its('response.statusCode').should('equal', 201)
// post 3rd time
cy.get('#post-user').click()
cy.wait('@postUser').its('response.statusCode').should('equal', 201)
// post 4th time
cy.get('#post-user').click()
cy.wait('@postUser').its('response.statusCode').should('equal', 201)
}) You could shorten this of course without using a custom command, something like const waitForStatus = (alias, code) =>
cy.wait(alias).its('response.statusCode').should('equal', code)
it('spies on multiple requests', () => {
cy.intercept({
method: 'POST',
pathname: '/users',
}).as('postUser')
cy.get('#post-user').click()
waitForStatus('@postUser', 201)
// post 2nd time
cy.get('#post-user').click()
waitForStatus('@postUser', 201)
// post 3rd time
cy.get('#post-user').click()
waitForStatus('@postUser', 201)
// post 4th time
cy.get('#post-user').click()
waitForStatus('@postUser', 201)
}) |
Hi @bahmutov, The example you gave is working, but can you confirm that what you show above works for cypress 6.0+, but if the scenario is tweaked a bit, then we I see some problems,
I actaully perform my "DO LOTS OF CY.GET" in a for-loop and when I do the tests on the first few conditions/elements, the test gets completed successfully. But, when I increase the number of cy.get test cases to +50 then, I see the XHR getting sent, it is also coming to the backend API but the cy.wait on the intercept fails. Should this be the case ? |
Can you clone the repo, put the code with lots of gets and give us the url to see? Because maybe my idea of lots is different and I don't see what you see. |
@bahmutov That works. You were right. For some reason, my custom command is the issue. My guess is it's a race-condition with the way cypress uses Promises? I REALLY want better support for:
That's why I wrote the custom command. On our project, we've got a frontend and backend team. Whenever a test fails, and it's because some DOM element doesn't exist, they (the backend team) throw the bug to us. We look into it and find that the DOM element doesn't exist because an endpoint call failed, and have to throw it back to them. It's much easier to diagnose Is there any way to fix my custom command while I continue to try to convince you guys to add this feature? I can use the arrow function in the meantime. Thanks for taking the time to figure this out btw. |
@bahmutov if I simplify my command to this, it works: Cypress.Commands.add('waitForStatus', (alias, code) =>
cy.wait(alias).its('response.statusCode').should('equal', code),
); It looks like maybe all this stuff with I think you have me on the right path. And I think this conversation is out of scope for the original purpose of this issue posting. Thanks for your help! Edit: Final iteration back to what I originally had: Cypress.Commands.add('waitWithStatus', (name: string, status: number) => {
return cy.wait(`${name}`).then((subject) => {
if (!subject.response) {
cy.log('No statuscode. Was this endpoint called more than once?');
} else if (subject.response?.statusCode !== status) {
cy.log('http response', subject.response?.body);
}
return cy.wrap(subject).its('response.statusCode').should('eq', status);
});
}); |
Hi @bahmutov Thanks for the quick reply. I setup something for you to check out. On my development VM (ram 2GB, just informing as its not that powerful), if I set DEFINE_LOT to 10 then all tests passes where us at 100 test fail. FYI, I have randomly placed the function call lotOfGets() either before, after or in between the click-action and the wait - so there is no particular reason why it is called how it is. The main point is, by chaning 10 to 100 the tests fail, which should not be the case. Also, in case of 100, the call sometimes fail on the third and sometimes on the fourth request. |
Hi @bahmutov Can you confirm if this problem is reproducible on your side ? Thanks for your support. |
I've been waiting for the ability to override |
The code for this is done in cypress-io/cypress#14513, but has yet to be released. |
This has been released in In order to mock different responses for requests that happen quickly, back to back - that is, there is no action between the requests, we recommend using the new it(`test ${i}`, () => {
cy.intercept('https://jsonplaceholder.typicode.com/todos/1', { times: 1 }, { title: 'baz' }).as('getTodo')
cy.intercept('https://jsonplaceholder.typicode.com/todos/1', { times: 1 }, { title: 'bar' }).as('getTodo')
cy.intercept('https://jsonplaceholder.typicode.com/todos/1', { times: 1 }, { title: 'foo' }).as('getTodo')
cy.visit('/')
cy.wait('@getTodo')
cy.wait('@getTodo')
cy.wait('@getTodo')
cy.get('#api-response-list li')
.first().should('have.text', 'foo')
.next().should('have.text', 'bar')
.next().should('have.text', 'baz')
}) There's currently another issue with the example above that I've found and already has a fix ready for it here: #16457 So after that is resolved, I think everyone should have a solution. This comment thread has been locked. If you are still experiencing this issue after upgrading to Cypress v7.3.0, please open a new issue with a reproducible example. |
Current behavior:
If you make multiple requests to the same endpoint and want to mock each response differently based on the order that they occur, cypress appears to be unable to do it unless you wait X milliseconds between requests. If there is no pause between requests, cypress will never catch the first
cy.wait(...)
and will mock every request to that endpoint with the same response.Desired behavior:
Should result in the first GET request to url responding with res1 and the second GET request to url responding with res2, regardless of the amount of time between requests.
Steps to reproduce: (app code and test code)
Run tests here
The text was updated successfully, but these errors were encountered: