如何在Cypress中验证API请求是否实际发起及发起次数?
Cypress验证API请求仅触发一次及后续无请求的正确做法
问题分析
你当前代码的核心问题:
- 每次调用
cy.spy()都会生成全新的间谍对象,之前拦截绑定的spy和断言时调用的cy.spy()不是同一个实例,自然无法正确断言 - 使用
cy.wait(1000)这种固定等待不是可靠的测试方式,应该基于Cypress的命令队列或请求拦截状态来判断
修正方案
要正确实现需求,需要:
- 创建一个可复用的spy实例并保存引用
- 用该spy替换拦截的响应处理函数
- 直接断言spy的调用次数,无需固定等待
修改后的测试代码:
it("update statement from the map is edited and sent only once", () => { cy.intercept("POST", mapsPath, { fixture: "initDefaultMap.json" }).as("saveMap"); cy.intercept("POST", lrsPath, { fixture: "statementOpened.json" }).as("statementUpdatedMap"); // 第一步操作,触发初始API请求 cy.get('[data-cy="canvas"]').click(); cy.get("zt-color-picker[icon='bg-color']").shadow().find("zt-button").shadow().find(".zt-button").click(); cy.get("zt-color-picker[icon='bg-color']") .shadow() .find("#color-picker-submenu > ul > li.color-boxes[data-color='#99FFCC']") .click(); cy.wait("@statementUpdatedMap").then((evt) => expect(extractVerb(evt)).to.be.equal("updated")); cy.wait("@saveMap"); // 创建spy实例并保存到变量 const lrsSpy = cy.spy().as("lrsSpy"); // 重新拦截请求,用spy处理响应 cy.intercept("POST", lrsPath, (req) => { lrsSpy(); // 触发spy计数 req.reply({ fixture: "statementOpened.json" }); // 保持原有响应 }); // 修改标题操作 cy.get('[data-cy="edit-title"]').click(); cy.get("body [data-cy='input-title'] input").clear().type("mappa bella"); cy.get('[data-cy="canvas"]').click(); // 断言spy未被调用 cy.get("@lrsSpy").should("not.have.been.called"); // 再次修改背景色操作 cy.get("zt-color-picker[icon='bg-color']").shadow().find("zt-button").shadow().find(".zt-button").click(); cy.get("zt-color-picker[icon='bg-color']") .shadow() .find("#color-picker-submenu > ul > li.color-boxes[data-color='#5533FF']") .click(); // 再次断言spy未被调用 cy.get("@lrsSpy").should("not.have.been.called"); });
关键改进点
- 复用spy实例:通过
const lrsSpy = cy.spy().as("lrsSpy")创建并命名spy,后续通过别名@lrsSpy复用同一个实例 - 拦截绑定spy:在
cy.intercept的回调中调用spy,确保每次匹配到请求时都会触发spy计数 - 可靠断言:用
cy.get("@lrsSpy").should("not.have.been.called")替代固定等待,Cypress会自动重试断言直到超时,保证测试稳定性
内容的提问来源于stack exchange,提问作者Oleksandr Oleksyuk
相关产品推荐
相关产品推荐

