javascript Cypress.io 如何处理异步代码

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/49980311/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 08:51:15  来源:igfitidea点击:

Cypress.io How to handle async code

javascripttestingasync-awaitcypress

提问by Daniel S?aby

I'm in the middle of process of moving our old capybara tests to cypress.io as our application is going SPA way.

我正在将我们的旧水豚测试迁移到 cypress.io,因为我们的应用程序正在采用 SPA 方式。

In our case we have over 2000 tests covering a lot of features. So common pattern to test feature is to have an user with created and published offer.

在我们的案例中,我们有 2000 多个测试涵盖了许多功能。因此,测试功能的常见模式是让用户创建和发布商品。

On the beginning I wrote case where cypress were going trough page and clicking everything. It worked but I saw that offer create + publish took almost 1,5 minute to finish. And sometimes we need multiple offers. So we have a test which takes 5 minutes and we have 1999 left to rewrite.

一开始我写了一个案例,其中柏树通过页面并点击所有内容。它起作用了,但我看到要约创建 + 发布花了将近 1.5 分钟才能完成。有时我们需要多个报价。所以我们有一个需要 5 分钟的测试,我们还有 1999 年要重写。

We came up with REST API to create offer and user, basically shortcut for test env preparation.

我们想出了 REST API 来创建报价和用户,基本上是测试环境准备的快捷方式。

I came to the point where everything is working using async/await. So here's the thing. If I want to use normal async JS code with cypress I get Error: Cypress detected that you returned a promise from a command while also invoking one or more cy commands in that promise.

我到了使用async/await. 所以这就是事情。如果我想在 cypress 中使用普通的异步 JS 代码,我会得到Error: Cypress detected that you returned a promise from a command while also invoking one or more cy commands in that promise.

Here's how it looks like:

这是它的样子:

    const faker = require('faker')
    import User from '../../support/User';

    describe('Toggle button for description offer', () => {
      const user = new User({
        first_name: faker.name.firstName(),
        last_name: faker.name.firstName(),
        email: `QA_${faker.internet.email()}`,
        password: 'xxx'
      })
      let offer = null

      before(async () => {
        await user.createOnServer()
        offer = await user.createOffer()
        await offer.publish()
      })

      beforeEach(() => {
        user.login()
        cy.visit(`/offers/${offer.details.id}`)
        cy.get('.offer-description__content button').as('showMoreButton')
      })

      it('XXX', function () {
        ...some test
      })
    })

This snippet works as expected. Firstly it fires before and creates whole env then when it's done it goes further to beforeEach and starts testing.

此代码段按预期工作。首先它在之前触发并创建整个 env 然后当它完成时它会进一步到 beforeEach 并开始测试。

Now I would like to merge before and beforeEach like

现在我想合并之前和之前每个都喜欢

  before(async () => {
    await user.createOnServer()
    offer = await user.createOffer()
    await offer.publish()
    user.login()
    cy.visit(`/offers/${offer.details.id}`)
    cy.get('.offer-description__content button').as('showMoreButton')
  })

Which will fail because of async keyword. Now the question is: how to rewrite it to use async/await and cypress commands together? I tried to rewrite it with normal Promise but It won't work too ...

由于 async 关键字,这将失败。现在的问题是:如何重写它以同时使用 async/await 和 cypress 命令?我试图用普通的 Promise 重写它,但它也行不通......

Any help appreciated.

任何帮助表示赞赏。

采纳答案by Guilherme Lemmi

Your problem stems from the fact that cypress commands are not promises, although behaving like promises.

你的问题源于这样一个事实,即cypress 命令不是 promises,尽管表现得像 promises。

I can think of two options:

我能想到两个选择:

  • Try to refactor your test code to not use async/await, as these commands don't behave as expected when running your code on cypress (check this bug). Cypress already has a whole way of dealing with async code as it creates a command queue that always run sequentially and in the expected order. That means you could observe the effects of your async code to validate that it happened before moving forward on your test. For instance, if User.createUserOnServermust wait a successful API call, add code to your test that will wait for the request to complete, using cy.server(), cy.route() and cy.wait(), like below:

    cy.server();
    cy.route('POST', '/users/').as('createUser');
    // do something to trigger your request here, like user.createOnServer()
    cy.wait('@createUser', { timeout: 10000});
    
  • Use another third-party library that changes how cypress works with async/await, like cypress-promise. This lib may help you to treat cypress commands as promises that you can awaitin your beforecode (read more about it in this article).

  • 尝试重构您的测试代码以不使用 async/await,因为在 cypress 上运行您的代码时,这些命令的行为不符合预期(检查此错误)。赛普拉斯已经有了处理异步代码的完整方法,因为它创建了一个始终按预期顺序运行的命令队列。这意味着您可以在继续测试之前观察异步代码的影响以验证它是否发生。例如,如果User.createUserOnServer必须等待成功的 API 调用,请使用cy.server()、cy.route() 和 cy.wait()将代码添加到您的测试中,以等待请求完成,如下所示:

    cy.server();
    cy.route('POST', '/users/').as('createUser');
    // do something to trigger your request here, like user.createOnServer()
    cy.wait('@createUser', { timeout: 10000});
    
  • 使用另一个第三方库来改变 cypress 与 async/await 的工作方式,例如cypress-promise。这个库可以帮助您将 cypress 命令视为您可以awaitbefore代码中的承诺(在本文中阅读更多相关信息)。

回答by isotopeee

I'm having a similar issue regarding async/awaitinside it/ testblocks. I solved my problem by wrapping the body inside an async IIFE:

我在/块中遇到了关于async/await的类似问题。我通过将 body 包裹在async IIFE 中解决了我的问题:ittest

describe('Test Case', () => {
  (async () => {
     // expressions here
  })()
})