5月28日 02:56

Cypress 如何处理动态内容等待?cy.wait() 与自动重试的最佳实践

在 Cypress 测试中,动态内容(AJAX 请求、异步渲染、第三方 API)是最常见的测试不稳定来源。核心解法是两个机制:cy.wait() 精确等待网络请求,以及 Cypress 内置的重试能力(retry-ability)。下面逐一说明。

cy.wait():精确等待网络请求

cy.wait() 的正确用途是等待已拦截的网络请求完成,而非硬编码等待时间。

基本用法

javascript
// 先拦截,再触发,最后等待 cy.intercept('POST', '/api/login').as('loginReq'); cy.get('#login-btn').click(); cy.wait('@loginReq'); // 等到该请求完成才继续

关键参数

  • timeout:超时时间,默认 5000ms,可按场景调整
  • response:可直接断言响应内容
javascript
cy.wait('@loginReq', { timeout: 8000 }) .its('response.statusCode') .should('eq', 200);

等待多个请求

javascript
cy.intercept('GET', '/api/user').as('userReq'); cy.intercept('GET', '/api/profile').as('profileReq'); cy.visit('/dashboard'); cy.wait(['@userReq', '@profileReq']);

常见错误

  • cy.wait(3000) 硬编码等待——这是反模式,应改为等待具体请求或元素状态
  • 别名未定义就 cy.wait('@xxx')——会直接报错
  • cy.wait() 内嵌套其他命令——会导致执行顺序混乱

重试能力:Cypress 的核心设计

Cypress 的重试机制和很多人理解的不一样。它不是"失败后重试 3 次",而是查询类命令在超时时间内持续重试直到断言通过。

工作原理

cy.get()cy.contains().should() 等查询命令会不断重新查询 DOM,直到找到匹配元素或超时。这不是固定的"3 次",而是在 defaultCommandTimeout(默认 4000ms)内持续尝试。

javascript
// 这行代码会在 4 秒内不断查询 #result 是否可见 cy.get('#result').should('be.visible');

配置超时

javascript
// 全局配置 Cypress.config('defaultCommandTimeout', 6000); // 单条命令单独设置 cy.get('#slow-element', { timeout: 10000 }).should('exist');

不可重试的命令

注意,cy.click()cy.type() 等动作类命令不会重试。如果元素还没出现就 click,会报错。正确做法是先确保元素存在:

javascript
// 错误:元素可能还没加载 cy.get('#submit').click(); // 正确:先等待元素可操作 cy.get('#submit').should('be.visible').click();

实战最佳实践

1. 优先用 cy.intercept + cy.wait 处理异步

javascript
cy.intercept('GET', '/api/data').as('dataReq'); cy.visit('/page'); cy.wait('@dataReq'); // 此刻数据已加载,后续断言稳定可靠

2. 用 should 断言代替硬等待

javascript
// 不要这样 cy.wait(2000); cy.get('.item').should('have.length', 5); // 应该这样——Cypress 自动等待直到满足条件 cy.get('.item').should('have.length', 5);

3. 等待加载状态消失

javascript
cy.get('.loading-spinner').should('not.exist'); cy.get('.data-table').should('be.visible');

4. 测试失败自动重试配置

javascript
// cypress.config.js module.exports = { retries: { runMode: 2, // CI 中失败重试 2 次 openMode: 0, // 本地开发不重试 }, };

cy.wait() 与重试能力的配合

两者解决不同问题:cy.wait() 等待已知网络请求完成,重试能力等待未知时间的元素出现。实际项目中两者配合使用:

javascript
// 典型模式:拦截请求 → 触发操作 → 等请求完成 → 断言 UI cy.intercept('POST', '/api/submit').as('submitReq'); cy.get('#form').within(() => { cy.get('input[name="email"]').type('test@example.com'); cy.get('button[type="submit"]').click(); }); cy.wait('@submitReq').its('response.statusCode').should('eq', 200); cy.get('.success-msg').should('contain', '提交成功');

核心原则:能等请求就等请求,不能等请求就用断言让 Cypress 自动重试,永远不要用固定时间等待。

标签:Cypress