如何在Cypress测试中等待成功响应

如何在Cypress测试中等待成功响应,cypress,e2e-testing,Cypress,E2e Testing,背景 我使用3个后端服务器为我的一个在线SaaS应用程序提供容错。所有重要的API调用,如获取用户数据,请联系所有3台服务器,并使用第一个成功解析响应的值(如果有) 导出函数GetSuccessValueorRow$( 可观测$:可观测[], tryuntillies=30000, ):可见{ 复赛( …可观测$.map(可观测$=>{ 返回可观察的$.pipe( 超时(tryuntillies), catchError(err=>{ 返回(err).pipe(延迟(5000),合并映射(_er

背景

我使用3个后端服务器为我的一个在线SaaS应用程序提供容错。所有重要的API调用,如获取用户数据,请联系所有3台服务器,并使用第一个成功解析响应的值(如果有)

导出函数GetSuccessValueorRow$(
可观测$:可观测[],
tryuntillies=30000,
):可见{
复赛(
…可观测$.map(可观测$=>{
返回可观察的$.pipe(
超时(tryuntillies),
catchError(err=>{
返回(err).pipe(延迟(5000),合并映射(_err=>throwError(_err));
}),
);
})
);
}
GetSuccessValueorRow$get调用如下:

const shuffledApiDomainList=['server1-domain'、'server2-domain'、'server3-domain';
const sessionInfo=wait RequestUtils.getsuccessvalueorhrow(
…(ShuffledApidDomainList.map(shuffledDomain=>this.http.get(`${shuffledDomain}/file/converter/comm/session/info`)),
).toPromise();
注意:如果一个请求的解析速度比其他请求快,通常情况下,
race
rxjs函数将取消其他两个请求。在Chrome dev的网络选项卡上,它看起来像下面所示,第一个发送的请求由于太慢而被取消

问题:

我使用/file/converter/comm/session/info(我们称之为Endpoint 1)来获取一些与用户相关的数据。此请求被发送到所有3个后端服务器。如果有一个解析,则剩余的2个请求将被取消,即返回null

在我的Cypress E2E测试中

cy.route('GET','/file/converter/comm/session/info')。as('getSessionInfo');
参观https://www.ps2pdf.com/compress-mp4');
cy.wait('@getSessionInfo')。其('status')。应('eq',200)
如果自getSessionInfo别名被挂接到一个请求上,而该请求最终被
getSuccessValueOrThrow$
取消,则此操作有时会失败,因为该请求不是成功的请求。下图显示了自getSessionInfo别名的3个请求中有1个成功,但自第一个请求失败后测试失败

在Cypress中,如何等待一个成功的请求,即status=200请求?

生成一个包含XHR的HTTP请求和响应属性的对象。您得到的错误是因为您在XHR对象中查找属性
status
,但它是响应对象的属性。您首先必须获取o响应对象:

cy.wait('@getSessionInfo').should(xhr => {
    expect(xhr.response).to.have.property('status', 200);
});

编辑:因为我们的后端使用graphql,所以所有调用都使用单个
/graphql
端点。所以我必须想出一个解决方案来区分每个调用

我是通过使用
cy.route()
onResponse()
方法,并在Cypress对象中累积数据来实现的:

然后您可以这样使用它:

cy.wrap(Cypress.env()).should('have.property', 'sessionInfo200');
const isOk = cy.wait("@getSessionInfo").then((xhr) => {
  return (xhr.status === 200);
});
生成一个包含XHR的HTTP请求和响应属性的对象。出现错误是因为您正在XHR对象中查找属性
status
,但它是响应对象的属性。您首先必须访问响应对象:

cy.wait('@getSessionInfo').should(xhr => {
    expect(xhr.response).to.have.property('status', 200);
});

编辑:因为我们的后端使用graphql,所以所有调用都使用单个
/graphql
端点。所以我必须想出一个解决方案来区分每个调用

我是通过使用
cy.route()
onResponse()
方法,并在Cypress对象中累积数据来实现的:

然后您可以这样使用它:

cy.wrap(Cypress.env()).should('have.property', 'sessionInfo200');
const isOk = cy.wait("@getSessionInfo").then((xhr) => {
  return (xhr.status === 200);
});

方法1

使用
.should()
回调,如果状态不是200,则重复调用
cy.wait

function waitFor200(routeAlias, retries = 2) {
  cy.wait(routeAlias).then(xhr => {
    if (xhr.status === 200) return // OK
    else if (retries > 0) waitFor200(routeAlias, retries - 1); // wait for the next response
    else throw "All requests returned non-200 response";
  })
}

// Usage example.
// Note that no assertions are chained here,
// the check has been performed inside this function already.
waitFor200('@getSessionInfo'); 

// Proceed with your test
cy.get('button').click(); // ...
方法2

修改您首先要测试的内容。 很有可能-页面上有一些信息会告诉用户操作是否成功。例如,显示/隐藏微调器或进度条,或者只是更新页面内容以显示从后端获取的新数据


因此,在这种方法中,您将完全删除
cy.wait()
,并将重点放在用户在页面上看到的内容上—对实际页面内容做一些断言。

方法1

使用
.should()
回调,如果状态不是200,则重复调用
cy.wait

function waitFor200(routeAlias, retries = 2) {
  cy.wait(routeAlias).then(xhr => {
    if (xhr.status === 200) return // OK
    else if (retries > 0) waitFor200(routeAlias, retries - 1); // wait for the next response
    else throw "All requests returned non-200 response";
  })
}

// Usage example.
// Note that no assertions are chained here,
// the check has been performed inside this function already.
waitFor200('@getSessionInfo'); 

// Proceed with your test
cy.get('button').click(); // ...
方法2

修改您首先要测试的内容。 很有可能-页面上有一些信息会告诉用户操作是否成功。例如,显示/隐藏微调器或进度条,或者只是更新页面内容以显示从后端获取的新数据

因此,在这种方法中,您将完全删除
cy.wait()
,并关注用户在页面上看到的内容-对实际页面内容进行一些断言。

我像这样等待:

cy.wrap(Cypress.env()).should('have.property', 'sessionInfo200');
const isOk = cy.wait("@getSessionInfo").then((xhr) => {
  return (xhr.status === 200);
});
我这样等待:

cy.wrap(Cypress.env()).should('have.property', 'sessionInfo200');
const isOk = cy.wait("@getSessionInfo").then((xhr) => {
  return (xhr.status === 200);
});

根据文档,它看起来像是xhr对象具有status属性。顺便说一句,问题与我如何获取状态无关。问题是,正如我在问题中提到的,它只是有时会失败。你是对的,我读得太快了。请看我编辑过的答案。希望有帮助。根据文档,它看起来像xhr对象具有status属性。顺便说一句,问题与我如何获得状态无关。问题是,正如我在问题中提到的,它有时会失败。你是对的,我读得太快了。请参阅我编辑的答案。希望它能有所帮助。