【问题标题】:How to wait for a successful response in Cypress tests如何在赛普拉斯测试中等待成功响应
【发布时间】:2020-05-11 00:53:14
【问题描述】:

背景

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

export function getSuccessValueOrThrow$<T>(
        observables$: Observable<T>[],
        tryUntilMillies = 30000,
    ): Observable<T> {
        return race(
            ...observables$.map(observable$ => {
                return observable$.pipe(
                    timeout(tryUntilMillies),
                    catchError(err => {
                        return of(err).pipe(delay(5000), mergeMap(_err => throwError(_err)));
                    }),
                );
            })
        );
    }

getSuccessValueOrThrow$ 调用如下:

const shuffledApiDomainList = ['server1-domain', 'server2-domain', 'server3-domain';
const sessionInfo = await RequestUtils.getSuccessValueOrThrow(
                        ...(shuffledApiDomainList.map(shuffledDomain => this.http.get<SessionDetails>(`${shuffledDomain}/file/converter/comm/session/info`))),
                    ).toPromise();

注意:如果一个请求比其他请求解决得更快,通常情况下,race rxjs 函数将取消另外两个请求。在 Chrome 开发网络选项卡上,第一次发出的请求由于太慢而被取消,如下所示。

问题:

我使用 /file/converter/comm/session/info(我们称之为端点 1)来获取一些与用户相关的数据。此请求已分派到所有 3 个后端服务器。如果一个解决了,那么剩下的 2 个请求将被取消,即它们将返回 null。

在我的赛普拉斯 E2E 测试中,我有

cy.route('GET', '/file/converter/comm/session/info').as('getSessionInfo');
cy.visit('https://www.ps2pdf.com/compress-mp4');
cy.wait('@getSessionInfo').its('status').should('eq', 200)

如果因为 getSessionInfo 别名被连接到最终被 getSuccessValueOrThrow$ 取消的请求,这有时会失败,因为它不是成功的请求。下图显示了 3 个使用 getSessionInfo 别名的请求是如何成功的,但是测试失败,因为第一个请求失败。

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

【问题讨论】:

    标签: cypress e2e-testing


    【解决方案1】:

    方法 1

    如果状态不是 200,则使用 .should() 回调并重复 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(),并专注于用户在页面上看到的内容 - 对实际页面内容进行一些断言。

    【讨论】:

      【解决方案2】:

      cy.wait() 生成一个包含 XHR 的 HTTP 请求和响应属性的对象。您收到的错误是因为您正在 XHR 对象中查找属性 status,但它是响应对象的属性。您首先必须到达响应对象:

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

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

      我通过使用cy.route()onResponse() 方法并在赛普拉斯environment 对象中累积数据来做到这一点:

      cy.route({
        method: 'GET',
        url: '/file/converter/comm/session/info',
        onResponse(xhr) {
          if (xhr.status === 200) {
            Cypress.env('sessionInfo200') = xhr;
          }
        }
      })
      

      然后你可以像这样使用它:

      cy.wrap(Cypress.env()).should('have.property', 'sessionInfo200');
      

      【讨论】:

      • 根据文档,它看起来像 xhr 对象具有状态属性docs.cypress.io/api/commands/…。顺便说一句,问题与我如何获得状态无关。问题是它有时会失败,正如我在问题中提到的那样。
      • 你说得对,我读得太快了。请参阅我编辑的答案。希望对您有所帮助。
      【解决方案3】:

      我就这样等待:

      const isOk = cy.wait("@getSessionInfo").then((xhr) => {
        return (xhr.status === 200);
      });
      

      【讨论】:

        猜你喜欢
        • 2023-02-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-07-15
        • 2023-03-23
        • 2020-05-09
        • 2021-02-16
        • 1970-01-01
        相关资源
        最近更新 更多