如何验证是否确实在Cypress中进行了调用

问题描述 投票:0回答:3

我正在尝试在 cypress 测试中检查 API 调用是否只进行一次。

然后我尝试继续进行更改,并且我想验证这些调用实际上并未进行。

  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");

    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");
    cy.intercept("POST", lrsPath, cy.spy());
    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();
    cy.wait(1000).then(() => expect(cy.spy()).not.to.be.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();
    cy.wait(1000).then(() => expect(cy.spy()).not.to.be.called);
  });

我尝试使用

cy.spy()
,但没有成功

cypress e2e-testing cypress-intercept
3个回答
6
投票

不幸的是,

cy.intercept()
不允许直接验证呼叫发生的固定次数。

如果我通过

POST
发送
cy.window()
来模拟您的代码,当我等待第二个从未发生的调用时,我可以看到测试失败:

const lrsPath = 'endpoint'   // for example

cy.intercept("POST", lrsPath, { fixture: "statementOpened.json" })
  .as("statementUpdatedMap");

// trigger the post (simulated)
cy.window().then(win => {
  win.fetch(lrsPath, { method: "POST" })
})

cy.wait("@statementUpdatedMap")  // wait for one call

cy.wait("@statementUpdatedMap")  // wait for another call

然后测试失败:

enter image description here


将失败的测试变成通过的测试

我可以通过验证是否引发了失败消息来将其转变为通过测试。

  • 使用
    cy.on('fail')
    捕获错误
  • 验证错误消息以确保它是正确的错误
  • 使用 Mocha
    done()
    函数来确保抛出错误(Mocha
    done()
    it()
    函数的参数)

我使用以下自定义命令

Cypress.Commands.add('verifyFailed', (expectedMessage, done) => {
  function handleFail(error) {
    const actual = error.message
    const passed = actual.includes(expectedMessage)
    const log = passed ? 'Correct error thrown' :
      `"${expected}", actual: "${actual}"`
    assert(passed, log)
    cy.off('fail', handleFail)
    done()
  }
  cy.on('fail', handleFail)
})

然后我在测试失败时使用它,在本例中是在第二个

cy.wait('@statementUpdatedMap')
之前。

it('turn a failing test into a passing test with Mocha done()', (done) => {

  const lrsPath = 'endpoint'   // for example

  cy.intercept("POST", lrsPath, { fixture: "statementOpened.json" })
    .as("statementUpdatedMap");

  // trigger the post (simulated)
  cy.window().then(win => {
    win.fetch(lrsPath, { method: "POST" })
  })

  cy.wait("@statementUpdatedMap")  // wait for one call

  cy.verifyFailed('No request ever occurred', done)
  cy.wait("@statementUpdatedMap")  // wait for another call
})

现在,当测试中的某一行失败时,测试通过了enter image description here

如果发生第二次调用确实,则测试将失败。

使用

done()
功能可防止出现 误报 结果。

enter image description here


4
投票

cy.get('@statementUpdatedMap.all')
是当前有效的语法,尽管如上所述尚未正式记录。它已经存在了一段时间,所以很有可能它会继续可用。

它的问题是,您只能在确定所有呼叫都有响应后才能运行它(

cy.intercept()
记录呼叫的响应阶段)。

因此您可以在调用之前添加等待,但有一些常见的警告:

cy.wait(2000)
cy.get('@statementUpdatedMap.all')  ...

在这个特定的示例中,

cy.intercepts()
都是存根的,因此添加等待可能是可以的 - 没有直接的网络参与来破坏测试。

话虽如此,在 CI 上运行仍然可能会导致失败,因为云服务器正在处理多个虚拟实例的多任务,并且可能需要几秒钟的时间来进行任务切换,因此在该环境中这可能不是一个好主意。

还有另一种使用 https://github.com/bahmutov/cypress-network-idle 的方法看起来很有用,但仍然需要指定等待时间。

有一个功能待处理的呼叫,看起来像是在观看请求而不是响应,这才是你真正想做的。 (自述文件页面上的链接已损坏,因此我直接链接到测试)。

  cy.waitForNetworkIdle('@all', 2000, { timeout: 6000 })
    .should('have.keys', 'started', 'finished', 'waited', 'callCount')
    .then(({ waited, callCount }) => {
      // the page makes the Ajax call that resolves after 3 seconds
      // with 2 seconds of checking for network idle makes 5 seconds
      expect(waited, 'waited ms').to.be.within(5000, 6500)
      // the document and the Ajax
      expect(callCount, 'callCount').to.equal(2)
    })

如果您想在不使用插件的情况下使用这个想法,这就是我在基本 Cypress 代码中的做法

let calls = 0;
cy.intercept("POST", mapsPath, (req) => {

  // record the call upon request (should happen very soon after the 2nd click
  calls++;

  // reply with fixture as per original code
  req.reply({
    fixture: 'initDefaultMap.json'
  })
}).as("saveMap");

...  // initial code to set up the click

cy.wait("@saveMap");   // wait for the first call

...  // repeat code to click again

cy.get(something-that-appears-on-the-page-after-clicking) 
  .should('be.visible')                            // preferred way to wait
  .then(() => {
    expect(calls).to.eq(1)
  })

cy.verifyFailed()
命令看起来非常可靠,并且不仅仅可以用于验证拦截调用。


-1
投票
可以通过

cy.intercept()

 查询别名(通过 
cy.as()
)的 
cy.get()
调用。我们可以将其与 (记录不足)
@alias.all
请求结合起来,以获取使用别名的整个次数列表(在本例中为触发拦截的次数),并使用该结果的长度来验证该电话只打过一次。

假设

statementUpdatedMap
别名是您要验证的调用仅被调用一次...

  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");

    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");
    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();
    cy.get('@statementUpdatedMap.all').should('have.length', 1); // validating length of yielded calls is one
    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();
    cy.get('@statementUpdatedMap.all').should('have.length', 1); // validating length of yielded calls is one  });
© www.soinside.com 2019 - 2024. All rights reserved.