重试机制是Cypress的核心概念,由于如今的web应用几乎都是异步加载的,那么如果断言发生的时候程序尚未更新DOM该如何处理?如果断言发生的时候应用程序正在等待其后端响应而导致页面暂时没有结果该如何处理?如果断言发生的时候应用程序正在进行密集计算导致页面未及时更新该如何处理?

断言重试

自动化测试过程中这些情况经常出现,而通常情况下的处理方式就是智能等待,而这种等待往往是硬编码仍然会出现失败,而Cypress是怎么处理的呢?

// testLogin.js
///<reference types="cypress"/>
describe('登陆', function(){
	// 此用户名密码为本地服务器默认
	const username = 'jane.lane'
	const password = 'password123'
	context('HTML表单登陆测试', function(){
		it('登陆成功, 跳转到dashboard页', function(){
			cy.visit('http://localhost:7077/login')
			cy.get('input[name=username]').type(username)
			cy.get('input[name=password]').type(password)
			cy.get('form').submit()
			// 断言,验证登陆成功则跳转到dashboard页面
			// 断言,验证用户名存在
			cy.url().should('include', '/dashboard')
			cy.get('h1').should('contain', 'jane.lane')
			})
		})
	})

就这段代码而言,cy.get()命令之后的断言如果通过则该命令成功结束,如果cy.get()命令后的断言失败,则cy.get()命令将重新查询应用程序的DOM,然后Cypress将尝试对cy.get()返回的元素进行断言,如果断言仍然失败,则cy.get()将尝试重新查询DOM,直至断言成功或者cy.get()命令超时为止

多重断言

所谓多重断言就是某个命令后跟多个断言,在这种情况下Cypress将按顺序重试每个命令,也就是说当第一个断言成功后在进行第二个断言时仍会重试第一个断言,以此类推,如下代码所示

cy.get('.list>li')
	.should('have.length', 2)
	.and(($li)=>{
		expect($li.get(0).textContent, 'first item').to.equal('iTesting')
		expect($li.get(1).textContent, 'second item').to.equal('testerTalk')
	})

代码中拢共3个断言,一个.should()和两个expect(),其中.and()断言实际上是.should()断言的别名,它是.should()的自定义回调断言,它包含两个expect()断言
实际执行过程中,如果第二个断言失败了,第三个断言永远不会执行,如果第二个断言失败的原因被找到且修复了,且此时整个命令还没有超时,那么在进行第三个断言前会再次重试第一个和第二个断言

重试条件

Cypress并不会重试所有的命令,当命令可能改变被测应用程序的状态时,这些命令将不会被重试,例如.click();Cypress仅会重试那些查询DOM的命令cy.get().find().contains()等等,可以通过Assertions来检查是否重试了特定命令,例如.first()命令会一直重试知道紧跟该命令后的所有断言都通过为止,常用的可重试命令如下

命令 命令 命令
.get() .last() .ParentsUntil()
.invoke() .children() .prev()
.contains() .next() .prevAll()
.find() .nextAll() .prevUntil()
.filter() .nextUntil() .focused()
.its() .not() .hash()
.eq() .parent() .closest()
.first() .parents() .document()

重试的超时时间默认为4秒,如果要更改超时时间可在cypress.json里找到defaultCommandTimeout,修改该字段值即可