在赛普拉斯的for循环中等待异步方法

时间:2018-10-10 14:56:18

标签: javascript asynchronous cypress

编辑:为什么这不是重复的:因为 Cypress ,请阅读而不是将所有内容标记为重复的

编辑2:此外,请参阅答案,以更好地理解常见的异步for循环问题与该问题之间的区别。


我正在编写cypress测试,我想创建一个cypress命令,该命令将用户列表填充到我的数据库中。我希望创建循环在每个用户转到下一个用户之前都等待它被创建(因为我希望按特定的顺序完成操作)。

现在,我的循环如下:

Cypress.Commands.add("populateDb", (users) => {
  var createdItems = []

  for (const user of users) {
    cy.createUser(user, 'passe').then(response => {
      createdUsers.unshift(response.body.user)
    })
  }
  return createdItems
})

当然,此循环不会等到创建每个用户后再转到下一个用户(我要“顺序处理”,“并行然后等待所有诺言解决”) )

我在这里阅读了有关异步for循环的答案:

但是我似乎找不到我想要的东西,主要是因为赛普拉斯不允许我按如下方式将我的函数声明为异步函数:

Cypress.Commands.add("populateDb", async (users) => {
    //Some code
})

如果我没有声明async,我将无法使用await

难道没有get()方法的国王只是同步地等待Promise解决吗?

3 个答案:

答案 0 :(得分:1)

结合使用wrapeach cypress命令,我可以实现一个循环,该循环等待每次迭代,并且可以返回完整结果,而无需全局变量。

我使用wrap命令的唯一原因是因为cypress命令each要求将其链接到先前的cypress命令。 each命令将评估每次迭代,最后调用then,您可以在其中返回完整的结果数组。我正在使用它上传多个文件并返回密钥列表,但是您可以根据自己的需要进行修改。

Cypress.Commands.add("uploadMultipleFiles", (files) => {
    var uploadedS3Keys = []
    cy.wrap(files).each((file, index, list) => {
        cy.uploadFileToOrdersApi(file)
            .then(s3Key => uploadedS3Keys.push(s3Key))
    }).then(() => uploadedS3Keys)
})

答案 1 :(得分:0)

事实证明,赛普拉斯如此严格地限制了等待异步方法解决的工作,原因是:它总是自动按调用的顺序顺序运行所有异步命令,而不是并行运行,因此即使createUser是异步的,也将以正确的顺序执行:

Cypress.Commands.add("populateDb", (users) => {
  for (const user in users) {
    cy.createUser(user).then(response => {
      console.log(response)
    })
  }
})

如果要获取返回值(在我的情况下,我需要用户ID以后再删除它们),则可以将它们存储在文件根级别的var中并添加一个赛普拉斯返回该变量的命令。

var createdItems = []

Cypress.Commands.add("getCreatedItems", () => {
  return createdItems
})

Cypress.Commands.add("populateDb", (users) => {
  for (const user in users) {
    cy.createUser(user).then(response => {
      createdItems.unshift(response) // I unshift() because I need to delete them in FILO order later
    })
  }
})

然后在cypress测试文件中,您可以按照执行它们的顺序调用它们:

cy.populateDb(users)
cy.getCreatedItems().then(response => {
  //response is my createdItems Array, and cy.getCreatedItems() will run only when cy.populateDb() is resolved
})

答案 2 :(得分:0)

您也可以这样做:

Cypress.Commands.add("populateDb", (users) => {
  for (const user in users) {
    cy.createUser(user).then(response => {
      createdItems.unshift(response) // I unshift() because I need to delete them in FILO order later
    })
  }

  return createdItems;
})

cy.populateDb(users).then(response => {
  // ... runs after populate ...
})

但是另一个答案也是正确的。实际上,每个cy.xxxxx命令都被添加到命令队列中,并且一个接一个地运行。如果在队列执行过程中调用了新的cy.xxxxx2命令,它们将被添加到队列的前面。

下面是一个简单的示例,用于解释cypress命令队列和同步代码的执行顺序:

const a = 1; // a == 1

cy.cmd1().then(() => {
  cy.cmd2().then(() => {
    a += 1; // a == 5
  });

  a += 1; // a == 3

  cy.cmd3()
  a += 1; // a == 4
});

cy.cmd4().then(() => {
  a += 1; // a == 6
});

a += 1; // a == 2

// at this point cypress command queue starts running the queue that is cmd1, cmd4

// 1. cmd1 runs and adds cmd2 and cmd3 to front of command queue also adds +2 to a
// 2. cmd2 runs and adds +1 to a
// 3. cmd3 runs
// 4. cmd4 runs and adds +1 to a
// ... all done ...

因此从此示例中您可以看到,在您的情况下,循环将串行执行,因为每个cy.createUser被添加到cypress命令队列中,然后依次执行。