
本文介绍如何使用 cypress 和 cypress-recurse 插件,在多页商品列表中自动查找指定名称的商品并点击,避免递归陷阱,提升测试稳定性和可维护性。
本文介绍如何使用 cypress 和 cypress-recurse 插件,在多页商品列表中自动查找指定名称的商品并点击,避免递归陷阱,提升测试稳定性和可维护性。
在 Cypress 测试中处理分页场景(如电商商品列表)是一个常见但易出错的需求:既要确保元素存在于当前页,又要在未找到时自动翻页、重试,直至定位或超时。原生 Cypress 不支持阻塞式循环,直接用递归函数(如 findInPage() 嵌套调用)会导致命令队列混乱、cy.get() 异步行为不可控、found 变量状态失效等问题,最终表现为“只翻页不点击”或测试随机失败。
推荐采用社区成熟方案 —— cypress-recurse 插件,它专为这类“重复断言 + 重试动作”场景设计,语义清晰、逻辑健壮、错误可追踪。
✅ 正确实现方式(推荐)
首先安装插件:
npm install -D cypress-recurse
在 cypress/support/e2e.js 中导入:
import 'cypress-recurse'
然后编写稳定、可读的分页查找逻辑:
const itemsSelector = '#product_list li div.produkti' // 定位到每个商品容器(非仅 h5 文本节点)
const nextPageSelector = '#pagination_next_bottom'
const targetProductName = 'Printer Brother MFP Laser HL1222WEYJ1'
// 递归查找:获取商品容器 → 断言是否包含目标文本 → 否则点击下一页
cy.recurse(
() => cy.get(itemsSelector), // 每轮获取当前页所有商品容器
(items) => expect(items).to.contain(targetProductName), // 断言目标文本存在(要求文本唯一)
{
post: () => cy.get(nextPageSelector).click().wait(1000), // 翻页后等待加载(可选 wait)
limit: 10, // 最大重试页数,防无限循环
timeout: 30000 // 总超时时间(ms)
}
)
.contains(targetProductName) // 在已确认存在的商品容器中精准过滤
.click() // 点击对应商品(自动作用于匹配的 <div.produkti>)</div.produkti>
⚠️ 注意事项与进阶建议
-
文本唯一性是关键:expect(items).to.contain(value) 依赖目标文本在页面中全局唯一。若多个商品名含相似关键词(如 "Laser" 出现在多个型号中),请改用 .each() 进行精确匹配:
cy.recurse( () => cy.get(itemsSelector), (items) => { let found = false cy.wrap(items).each($item => { if (Cypress.$($item).find('h5').text().trim() === targetProductName) { found = true } }) expect(found).to.be.true }, { post: () => cy.get(nextPageSelector).click(), limit: 10 } ) .filter(`:contains('${targetProductName}')`) .find('h5') .click({ force: true }) 稳定性增强:建议在 post 中添加 cy.wait(1000) 或更优的显式等待(如 cy.get('.loading-spinner').should('not.exist')),确保分页加载完成后再执行下一轮断言。
错误反馈更友好:cypress-recurse 默认会在超时后抛出清晰错误信息(如 "Timed out retrying after 30000ms"),便于快速定位是数据不存在还是选择器错误。
替代方案(无插件):若无法引入插件,可借助 cy.then() + 自定义递归函数 + Cypress.Promise 封装,但复杂度显著上升,且需手动管理重试计数与异步链,不推荐初学者使用。
通过 cypress-recurse,你将告别脆弱的手动递归,以声明式语法专注业务逻辑 —— “找得到就点,找不到就翻页”,简洁、可靠、符合 Cypress 的命令式编程哲学。











