Skip to main content
Cypress应用

测试重试

info
你将学到
  • 测试重试的工作原理
  • 如何配置测试重试
  • 如何在截图和视频中使用测试重试
  • 如何在Cypress Cloud中使用测试重试

Cypress擅长测试复杂系统。然而,仍存在难以验证的行为会导致测试不稳定(即不可靠)并因不可预测的条件而失败。一些常见的竞态条件可能导致不可靠的测试包括:

  • 动画
  • API调用
  • 测试服务器/数据库可用性
  • 资源依赖可用性
  • 网络问题

通过测试重试功能,Cypress能够重试失败的测试以帮助检测测试不稳定性和持续集成(CI)构建失败。这将为团队节省宝贵的时间和资源,使您能专注于最重要的事情。

工作原理

info

Cypress 13.4.0引入的实验性测试重试功能检测不稳定测试提供了更多选项。实验性重试让您可以控制测试何时应标记为通过或失败的条件。

默认情况下,测试失败时不会重试。您需要在配置中启用测试重试才能使用此功能。

const { defineConfig } = require('cypress')

module.exports = defineConfig({
retries: 2,
})

启用测试重试后,可以配置测试进行X次重试尝试。例如,如果配置了2次重试尝试,Cypress将在可能标记为失败测试前最多重试测试2次(总共3次尝试)。

当每次重新运行测试时,以下钩子也会重新运行:

  • beforeEach
  • afterEach

beforeafter钩子中的失败不会触发重试。

以下是测试重试工作原理的详细分步示例:

假设我们配置了2次重试尝试(总共3次尝试),以下是测试可能的运行方式:

  1. 测试首次运行。如果测试通过,Cypress将照常继续运行剩余测试。

  2. 如果测试失败,Cypress会告知第一次尝试失败,并将尝试第二次运行测试。

  3. 如果第二次尝试后测试通过,Cypress将继续运行剩余测试。

  4. 如果测试第二次失败,Cypress将进行第三次也是最后一次重试。

  5. 如果测试第三次失败,Cypress将标记测试为失败,然后继续运行剩余测试。

测试重试第三次尝试失败

以下是通过cypress run运行相同失败测试时的屏幕截图。

显示测试重试的CLI错误信息

cypress open期间,您可以在命令日志中查看尝试次数,并根据需要展开每次尝试进行审查和调试。

配置测试重试

全局配置

通常您会希望为cypress runcypress open定义不同的重试尝试次数。您可以在Cypress配置中通过传递retries选项对象来配置:

  • runMode:定义运行cypress run时的测试重试次数
  • openMode:定义运行cypress open时的测试重试次数
const { defineConfig } = require('cypress')

module.exports = defineConfig({
retries: {
// 配置`cypress run`的重试次数
// 默认为0
runMode: 2,
// 配置`cypress open`的重试次数
// 默认为0
openMode: 0,
},
})

为所有模式配置重试次数

如果想为cypress runcypress open中的所有测试配置重试次数,可以在Cypress配置中定义retries属性并设置所需的重试次数。

const { defineConfig } = require('cypress')

module.exports = defineConfig({
retries: 1,
})

自定义配置

单个测试

如果想为特定测试配置重试次数,可以使用测试配置进行设置。

// 为单个测试自定义重试次数
describe('用户注册和登录', () => {
// 无自定义配置的`it`测试块
it('应将未认证用户重定向到登录页面', () => {
// ...
})

// 带自定义配置的`it`测试块
it(
'允许用户登录',
{
retries: {
runMode: 2,
openMode: 1,
},
},
() => {
// ...
}
)
})

测试套件

如果想为一组测试配置重试次数,可以通过设置套件的配置来实现。

// 为测试套件自定义重试次数
describe(
'用户银行账户',
{
retries: {
runMode: 2,
openMode: 1,
},
},
() => {
// 套件级配置会应用到每个测试
// 如果测试失败,将会重试
it('允许用户查看交易记录', () => {
// ...
})

it('允许用户编辑交易记录', () => {
// ...
})
}
)

更多关于自定义配置的信息请参考:测试配置

截图

当测试重试时,Cypress会继续为每次失败的尝试或cy.screenshot()截图,并为每个新截图添加(attempt n)后缀,对应当前的重试尝试次数。

使用以下测试代码,当所有3次尝试都失败时,您会看到以下截图文件名:

describe('用户登录', () => {
it('显示登录错误', () => {
cy.visit('/')
cy.screenshot('user-login-errors')
// ...
})
})
// 第一次尝试的cy.screenshot()截图文件名
'user-login-errors.png'
// 第一次失败尝试的截图文件名
'user-login-errors (failed).png'
// 第二次尝试的cy.screenshot()截图文件名
'user-login-errors (attempt 2).png'
// 第二次失败尝试的截图文件名
'user-login-errors (failed) (attempt 2).png'
// 第三次尝试的cy.screenshot()截图文件名
'user-login-errors (attempt 3).png'
// 第三次失败尝试的截图文件名
'user-login-errors (failed) (attempt 3).png'

视频

您可以使用Cypress的after:spec事件监听器,该监听器在每个spec文件运行后触发,删除没有重试尝试或失败的spec录制的视频。运行后删除通过且未重试的视频可以节省机器资源空间,并跳过处理、压缩和上传视频到Cypress Cloud的时间。

仅上传有失败或重试测试的spec视频

以下示例展示如何在使用Cypress测试重试时,删除没有重试尝试或失败的spec录制的视频。

const { defineConfig } = require('cypress')
// 需要安装这些依赖
// npm install lodash del --save-dev
const _ = require('lodash')
const del = require('del')

module.exports = defineConfig({
// setupNodeEvents can be defined in either
// the e2e or component configuration
e2e: {
setupNodeEvents(on, config) {
on('after:spec', (spec, results) => {
if (results && results.video) {
// 是否有任何重试尝试失败?
const failures = _.some(results.tests, (test) => {
return _.some(test.attempts, { state: 'failed' })
})
if (!failures) {
// 如果spec通过且没有测试重试,则删除视频
return del(results.video)
}
}
})
},
},
})

Cypress Cloud

如果使用Cypress Cloud,与测试重试相关的信息会显示在运行的测试结果选项卡上。选择Flaky过滤器将显示在运行期间重试后通过的测试。

这些测试还会在最新运行页面和运行详情页面的测试结果选项卡上标有"Flaky"徽章。

点击测试结果将打开测试用例历史屏幕。这会显示失败尝试的次数、失败尝试的截图和/或视频,以及失败尝试的错误。

不稳定测试产物和错误

您还可以查看给定测试的Flaky Rate。

不稳定率

要全面了解不稳定测试如何影响整个测试套件,您可以查看测试不稳定管理指南中强调的Flake DetectionFlake Alerting功能。

tip
调试Cypress Cloud测试运行?

不要依赖本地复现失败条件或人工解析测试产物。使用 测试回放功能,完整重现录制运行期间的测试执行过程,获得全面的调试能力。

常见问题解答(FAQs)

重试的测试会在账单中计为多个测试结果吗?

不会。使用--record标志运行cypress run时记录的测试,无论是否重试,计数方式相同。

我们认为每次调用it()函数都是一个测试用于计费目的。测试重试不会在账单中计为额外的测试结果。

您始终可以从Cypress Cloud中组织的计费与使用页面查看已记录的测试数量。

可以从测试中访问当前的重试计数器吗?

可以,尽管通常不需要,因为这是低级细节。但如果想使用当前尝试次数,可以使用Cypress.currentRetry。如果想确定允许的总尝试次数,可以执行以下操作:

it('在重试时执行不同的操作', { retries: 3 }, () => {
// Cypress.currentRetry返回当前测试重试计数
const attempt = Cypress.currentRetry
// cy.state('runnable')返回当前测试对象
// 我们可以从其属性中获取允许的总尝试次数
const retries = cy.state('runnable')._retries
// 以某种方式使用"attempt"和"retries"值
})

上面的attempt变量将有0到3的值(第一次默认测试执行加上三次允许的重试)。在这种情况下,retries常量始终为3。

提示: Cypress捆绑了Lodash库。使用其辅助方法安全访问对象的属性。让我们确保函数支持不同的Cypress版本,通过回退到默认值。

it('在重试时执行不同的操作', { retries: 3 }, () => {
// _.get: 如果对象或属性缺失,使用提供的默认值
const attempt = Cypress.currentRetry
const retries = Cypress._.get(cy.state('runnable'), '_retries', 0)
// 以某种方式使用"attempt"和"retries"值
})