【问题标题】:Cypress - adding retries to page object赛普拉斯 - 向页面对象添加重试
【发布时间】:2021-10-13 10:48:17
【问题描述】:

为了使以后的更改更容易,我们将登录脚本放在页面对象中。

//Login.js

export class Login {

username_input=         () => cy.get('#LoginForm_username');
password_input=         () => cy.get('#LoginForm_password');
login_button=           () => cy.contains('Login');
profile=                () => cy.get('.profile-content');

login(email, password){
    this.username_input().type(email);
    this.password_input().type(password);
    this.login_button().click();
    this.profile().should('exist')
        return this;
    }
}

所以以后我们可以在任何规范文件中重复使用它

//actual.spec.js

import {Login} from "../../pages/login/Login";

it('logs in', () => {
login.login(Cypress.env('userEmail'), Cypress.env('userPass'))
}

现在我们的登录页面出现了有时没有响应的奇怪行为。因此我们添加了重试。

我们找到了两种可行的方法,但都不理想:

解决方案 1:

将重试参数放在配置文件中

"retries": 2

为什么这不理想?

这会为每个测试启用重试,这是我们不希望的。我们只希望它用于登录脚本。

解决方案 2:

将重试参数放在'it'中

import {Login} from "../../pages/login/Login";

it('logs in', {retries:2} () => {
login.login(Cypress.env('userEmail'), Cypress.env('userPass'))
}

为什么这不理想?

我们必须将参数放在每个规范文件中,如果我们想更改重试次数或完全摆脱重试,我们需要在每个规范文件中更改它。

解决方案 3???

我现在正在寻找一种将重试参数放在login.js 的登录功能中某处的方法,但我找不到这样做的方法。

【问题讨论】:

    标签: javascript cypress retry-logic


    【解决方案1】:

    更多想法:

    即时重试更改

    TLDR
    优点:简单
    缺点:使用内部命令

    有一个内部命令可让您在测试中更改重试次数。

    我强调内部这个词是为了警告它可能会在某些新版本中停止工作。

    使用您的代码

    export class Login {
      ...
    
      login(email, password) {
    
        const originalRetries = cy.state('runnable')._retries
    
        cy.state('runnable')._retries = 5    // WARNING - internal command
        
        this.username_input().type(email);
        ...
    
        cy.state('runnable')._retries = originalRetries  // restore configured retries value
      }
    }
    

    递归重试

    TLDR
    优点:适用于不稳定的情况
    缺点:需要显式等待

    如果您想要更长但更主流的解决方案,请使用递归。

    您将需要一个非失败检查来处理重试或完成逻辑,这实际上意味着将 cy.get('.profile-content').should('exist') 更改为 jQuery 等效项并使用显式等待(此模式的缺点)。

    export class Login {
      ...
    
      loginAttempt(attempt = 0) {       
        
        if (attempt === 3) throw 'Unable to login after 3 attempts'
    
        this.login_button().click();
        
        // wait for login actions to change the page
        cy.wait(200).then(() => {  
          
          // test for success
          if (Cypress.$('.profile-content').length > 0) {
            return // found it, so finish
          }
    
          // retry
          loginAttempt(email, password, ++attempt)  
        })
      }
    
      login(email, password) {
        this.username_input().type(email);  
        this.password_input().type(password);
        loginAttempt()
        return this
      }
    }
    

    您可以将cy.wait(200) 降低一点并提高最大尝试次数,例如cy.wait(20)if (attempt === 300)

    缺点是每次重试都会占用更多的堆内存,因此过分使用会造成不利影响 - 您需要进行试验。


    直接设置登录状态

    TLDR
    好处:绕过测试的片状部分
    缺点:需要了解登录状态

    您可能会考虑的另一个方面是完全消除不稳定的登录。为此,您需要找出应用程序认为已登录的内容。是cookie、localstorage值等吗?

    对于每个需要处于登录状态但实际上并未测试登录过程的测试,直接设置该状态。

    这里给出一个例子recipes - logging-in__using-app-code

    describe('logs in', () => {
      it('by using application service', () => {
        cy.log('user service login')
    
        // see https://on.cypress.io/wrap
        // cy.wrap(user promise) forces the test commands to wait until
        // the user promise resolves. We also don't want to log empty "wrap {}"
        // to the command log, since we already logged a good message right above
        cy.wrap(userService.login(Cypress.env('username'), Cypress.env('password')), {
          log: false,
        }).then((user) => {
        // the userService.login resolves with "user" object
        // and we can assert its values inside .then()
    
          // confirm general shape of the object
          expect(user).to.be.an('object')
          expect(user).to.have.keys([
            'firstName',
            'lastName',
            'username',
            'id',
            'token',
          ])
    
          // we don't know the token or id, but we know the expected names
          expect(user).to.contain({
            username: 'test',
            firstName: 'Test',
            lastName: 'User',
          })
        })
    
    function login (username, password) {
      const requestOptions = {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ username, password }),
      }
    
      return fetch(`${config.apiUrl}/users/authenticate`, requestOptions)
      .then(handleResponse)
      .then((user) => {
        // login successful if there's a jwt token in the response
        if (user.token) {
          // store user details and jwt token in local storage to keep user logged in between page refreshes
          localStorage.setItem('user', JSON.stringify(user))
        }
    
        return user
      })
    }
    

    【讨论】:

    • 这简直太棒了!感谢您对不同选项进行了极其详细的细分。我将立即开始尝试这 3 种解决方案,看看是否可以让其中一种或多种发挥作用。理想情况下,我可以让它直接设置登录状态。我会及时通知你:)
    • 即时重试效果很好!我只是有点担心你关于它可能会停止工作新版本的评论。递归重试让我很难过。它似乎除了点击什么都不做。它绕过 cy.wait() 以及电子邮件和密码输入,只返回 3 次失败的尝试。增加 cy.wait() 什么也没做。不幸的是,除了尝试 1、2、3 失败外,它几乎没有输出任何报告。需要对此进行更多研究。仍在研究最后一个解决方案,但需要开发人员来帮助我
    • 我在递归中犯了一个错误 - 在看到另一个答案后调整了代码。
    • 我个人会使用cy.state('runnable')._retries,但我必须为阅读此代码的任何人附加警告。希望赛普拉斯将其添加为官方配方,因为您已经确定了现有重试模式中的差距。
    • 是的,这是我目前使用的解决方案。它就像我需要的那样工作。我将在代码中使用警告进行注释。
    【解决方案2】:

    (编辑帖子以包含第二个解决方法...)

    cypress 的整个想法是避免片状测试。如果您的登录有时会失败,有时不会,那么您甚至在开始之前就处于不稳定的环境。

    1. 如果您的所有测试都依赖于登录,那么在套件级别(在描述中)包含重试可能不是一个坏主意,但如果您的一些测试依赖于登录,那么您可以将它们全部分组一个套件,然后在套件级别添加重试。

    有点像..

    
    describe('Your suite name', {
      retries: {
        runMode: 2,
        openMode: 2,
      }
    }, () => {
     
    //number of retries will be applied per test
      it('logs in', () => {
        login.login(Cypress.env('userEmail'), Cypress.env('userPass'))
      })
      
      it('other test', () => {
    })
    
    })
    
    1. 通过 API 进行登录,并将其作为依赖登录的测试的一部分(因此它不必通过 UI,您不会遇到问题)。在测试登录本身时,仅在此处重试该测试。

    【讨论】:

    • 将重试从 it('logs in') 移动到 describe('') 部分基本上是解决方案 #1 和解决方案 #2 的混合,两者都有缺点。这意味着我们仍然需要在每个规范文件中进行重试(并在需要时在每个文件中进行更改),现在它会重试套件中的所有测试,这也是不可取的。
    • 还有另一种解决方案(但您不会每次都测试登录 UI),它通过 API 登录,然后继续您的测试,并且仅在一个规范中测试登录 UI .这将意味着您只为该规范添加重试次数,其余的应该可以工作。除非你的问题是更大的 API 相关蚂蚁
    • 这实际上可能是最好的主意!需要再次考虑通过 POST 登录。我们有点放弃了,因为我们还无法绕过 CSRF 登录。尝试了来自github.com/cypress-io/cypress-example-recipes/tree/master/… 的所有脚本,但没有一个有效。但这是另一个话题。猜猜这个解决方案是最好的一个
    • 很高兴我能帮上忙 :)
    【解决方案3】:

    当你写作时:

    现在我们的登录页面出现了有时不响应的奇怪行为。

    您可以放心,您的测试不是问题。

    【讨论】:

    • 好吧,我必须按照我得到的工作。
    猜你喜欢
    • 2021-10-14
    • 2018-11-14
    • 1970-01-01
    • 2021-02-26
    • 1970-01-01
    • 2021-01-02
    • 2023-02-08
    • 2020-05-30
    • 1970-01-01
    相关资源
    最近更新 更多