cypress intercepting specific api call from same url - cypress

In my scenario, I am polling a specific url, let's say http://example.com/search.
The response code returned is 202, and when the API has finished processing the task, it will eventually return status code 200.
My question is how do I assert that I eventually got status code 200, when the URL is the same for both the 202 and 200 responses?
Thanks,

It looks like you can to set times property to 2 and then wait two times:
cy
.intercept('/search', { times: 2 })
.as('search');
// ...
cy
.wait('#search')
.its('response.statusCode')
.should('eq', 200);
cy
.wait('#search')
.its('response.statusCode')
.should('eq', 202);

Related

Return status code when maximum retries attempted using backoff

Using asyncio and aiohttp, I have implemented an async function that triggers an API get request whenever a new record is inserted into database. If the request is successful, the status code has to be updated in database, otherwise the request should be retried 4 times and if it still fails, then the status code has to be updated in database.
To raise the exception on 404 status code, I have added raise_for_status flag to the aiohttp client session. When exception arises, the backoff decorator will retry the API call 4 times and when it still fails, it doesn't return any status code. This is what I have done:
# backoff decorator to retry failed API calls by "max_tries"
#backoff.on_exception(backoff.expo, aiohttp.ClientResponseError, max_tries=4, logger=logger)
async def call_url(language: str, word:str, headers:dict) -> bytes:
url = f"https://od-api.oxforddictionaries.com:443/api/v2/entries/{language}/{word.lower()}"
print(f"Started: {url}")
# Create aiohttp session to trigger 'get' dictionary API call with app_id, app_key as headers
async with aiohttp.ClientSession(headers=headers) as session:
# raise_for_status is used to raise exception for status_codes other than 200
async with session.get(url, raise_for_status=True) as response:
# Awaits response from dictionary API
content = await response.read()
status = response.status
print("Finished: ", status)
# Returns API status code to be updated in db
return status
I can't add the try-except clause because once exception is raised, it's handled by the try-except clause and the backoff doesn't retry the failed API call. Is there a way to make backoff decorator return the status code after maximum retries are attempted?
You say that "when it still fails it doesn't return any status code." But it has to either return something or else raise an exception. How about wrapping the call_url and handling the error situation in the wrapper function? The wrapper function always returns a status code. For instance:
async def my_call_url(language: str, word:str, headers:dict) -> bytes:
try:
return await call_url(language, word, headers)
except WhateverExceptionAiohttpRaises:
return some_status_code
If, instead of raising, the decorator returns None, you can make the appropriate changes.
Your code will now call this new function instead of the other one.

How to set http status code 200, 400, 422, 202 set as success in JMeter

In my test plan I want to mark all the response with, http status code 200, 400, 422, 202 set as success.
Is there any way I can achieve this in a single assertion ?
You should also include what you have tried so far, to resolve this it is very simple,
Need to include a JSR223 assertion with the following script,
if("400".equals(SampleResult.getResponseCode()) || "200".equals(SampleResult.getResponseCode()) || "202".equals(SampleResult.getResponseCode()) || "422".equals(SampleResult.getResponseCode())) {
SampleResult.setSuccessful(true);
AssertionResult.setFailure(false);
}
else {
AssertionResult.setFailure(true);
}
Another solution could be with a Response Assertion
Add the Response Assertion into the Test Plan. This will ensure the Assertion is applied to all the Samplers (Responses)
Then configure the Response Assertion
Field to Test as Response Code
Pattern matching rules to Equals and Or
Click the Add button and add the response codes
In addition to the above configuration, you may check the Ignore Status if you want to Instructs JMeter to set the status to success initially.
Here is another solution based on the previous answer.
//Expected Response codes
def lstExpectedResponseCodes= ["400", "200", "202","422"]
//Actual Response code
String actualResponseCode=SampleResult.getResponseCode()
boolean hasResponseCode = lstExpectedResponseCodes.contains(actualResponseCode)
AssertionResult.setFailure(!hasResponseCode)
SampleResult.setSuccessful(hasResponseCode);

Handle negative cases in JMETER, for example my expected output response is 400

How to handle negative cases in JMETER, for example my expected output response is 400("There are no records") for an GET API?
In JMETER response is coming as failure or warning.
Is JMeter only handle positive scenarios like for all GET API response code should be 200 ?
Add Response Assertion as a child of the HTTP Request sampler which returns HTTP Status Code 400
Configure it as follows:
Tick Ignore status box
Set "Field to test" to Response code
Set "Pattern matching rules" to Equals
Add 400 as a "Pattern to test"
This way JMeter will pass only if the parent HTTP Request sampler returns 400 status code, otherwise it will fail.
You can add to HTTP Request Response Assertion with Ignore status checked
HTTP Responses with statuses in the 4xx and 5xx ranges are normally regarded as unsuccessful. The "Ignore status" checkbox can be used to set the status successful before performing further checks. Note that this will have the effect of clearing any previous assertion failures, so make sure that this is only set on the first assertion.
I tried with this, by adding a BeanShell Assertion with following code.
import org.apache.jmeter.assertions.AssertionResult;
String failureMessage = "";
String ResCode = SampleResult.getResponseCode();
if (!ResCode.equals("400")) {
failureMessage = "Got Response Code" + ResCode;
AssertionResult result = new AssertionResult("Expected Response 400");
result.setFailure(true);
result.setFailureMessage(failureMessage);
prev.addAssertionResult(result);
prev.setSuccessful(false);
SampleResult.setStartNextThreadLoop(true);
} else {
//failure criteria
}

Hold stubbed response till cypress assertion succeeds, avoid response delay

I would like to test my "waiting for server reply" UI behavior. How do I do that reliably without pausing my test for a hardcoded delay?
Say, I have a button that triggers an http request and has to show certain animation / actions until the response arrives.
A dumb working method is:
cy.route({
delay: 1000,
response: "blah blah",
})
// triggger submission
cy.get('#my-submit-button').click()
// will disappear upon receiving response
cy.contains('Waiting for response...')
I am pretty much sure that the "waiting" text will appear within a second while the response paused, but then I commit the sin of pausing the test for a whole second.
If I start to shorten or remove the delay then I am running a risk of creating flaky tests since there's a chance the response would be processed before I check for the existence of the "Waiting..." text, which would've been removed by that moment.
Is there a way to ensure the response is produced only after the check for the "Waiting..." text without a hard delay?
I naïvely tried to do a cypress assertion from the route's onResponse, but cypress wasn't happy about that:
cy.route({
onResponse: xfr => {
cy.contains('Waiting for response...')
return xfr
},
response: "blah blah",
})
cy.get('#my-submit-button').click()
producing the https://on.cypress.io/returning-promise-and-commands-in-another-command error:
Error: Uncaught CypressError: Cypress detected that you returned a promise from a command while also invoking one or more cy commands in that promise.
The command that returned the promise was:
> cy.click()
The cy command you invoked inside the promise was:
> cy.contains()
Because Cypress commands are already promise-like, you don't need to wrap them or return your own promise.
Cypress will resolve your command with whatever the final Cypress command yields.
The reason this is an error instead of a warning is because Cypress internally queues commands serially whereas Promises execute as soon as they are invoked. Attempting to reconcile this would prevent Cypress from ever resolving.
Could this work?
cy.route({
delay: 1000,
response: "blah blah",
}).as('getResponse')
cy.wait('getResponse')
https://docs.cypress.io/guides/guides/network-requests.html#Waiting
You could record the time these things happen using cy.moment(), then compare it afterwards using .isBefore() or one of the other moment functions.
I'm using onResponse here to record the time the response comes in.
let timeDisappeared;
let timeResponded;
cy.route({
delay: 1000,
response: "blah blah",
onResponse: () => {
timeResponded = Cypress.moment()
}
})
// triggger submission
cy.get('#my-submit-button').click()
// waits for the text to appear
cy.contains('Waiting for response...').should('exist')
// waits for the text to disappear
cy.contains('Waiting for response...').should('not.exist').then(() => {
timeDisappeared = Cypress.moment()
})
expect(timeResponded.isBefore(timeDisappeared))
After saw your comment and check the other answers
I think Bredan's answer is the best solution to do this. You can
stub a response with delay: x seconds
check the text to appear. Record the time: timeAppear
check the text to disappear. Record the time: timeDisappear
Check if timeDisappear - timeAppear > x seconds
or Check timeDisappear - timeAppear - x seconds > certain seconds(You can define the tolerance)
This proves the text shows in the given response time.
You can extend the response Delay to longer value.
I modified Bredan's answer a bit to reflect the steps above,
let timeAppear;
let timeDisappear;
//delay the response to come back after 20 seconds
cy.route({
delay: 20000,
response: "blah blah"
})
// triggger submission
cy.get('#my-submit-button').click()
// waits for the text to appear and record the time starts.
cy.contains('Waiting for response...').should('exist').then(() => {
timeAppear = Cypress.moment()
})
// waits for the text to disappear and record the time ends
// add timeouts option here to make sure it is longer than delay
cy.contains('Waiting for response...',{timeout:30000}).should('not.exist').then(() => {
timeDisappear = Cypress.moment();
//check the time is above the delay time 2000 ms
expect(timeDisappear - timeAppear).to.be.above(20000);
//or check the tolerance value e.g.5000 ms
expect(timeDisappear - timeAppear - 20000).to.be.below(5000);
})

how to use bluebirdJS promises to make API requests in BATCHES instead of just limiting by concurrency

I have 1000 http API requests to be made. I have kept them all as promises in an array. I want to execute them in "BATCHES" of 100 at a time - not more than that to avoid hitting any API rate-limit / throttling etc.
While bluebirdJS provides the .map() function with the concurrency option what it does is it limits the number of calls made AT A TIME. Meaning it will ensure that no more than 100 concurrent requests are being worked on at a time - as soon as the 1st request resolves, it will begin processing the 101st request - it doesn't wait for all the 100 to resolve first before starting with the next 100.
The "BATCHING" behavior i am looking for is to first process the 100 requests, and ONLY AFTER all of the 100 requests have completed it should begin with the next 100 requests.
Does BlueBirdJS provide any API out of the box to handle batches this way?
You can split big urls array to an array of batches. For each batch run Promise#map which will be resolved when all async operations are finished. And run these batches in sequence using Array#reduce.
let readBatch(urls) {
return Promise.map(url => request(url));
}
let read(urlBatches) {
return urlBatches.reduce((p, urls) => {
return p.then(() => readBatch(urls));
}, Promise.resolve());
}
const BATCH_SIZE = 100;
let urlBatches = [];
for (let i = 0; i < urls.length; i+= BATCH_SIZE) {
let batch = array.slice(i, i + BATCH_SIZE);
urlBatches.push(batch);
}
read(urlBatches)
.then(() => { ... }) // will be called when all 1000 urls are processed

Resources