【问题标题】:Intercept the same API call multiple times in Cypress在 Cypress 中多次拦截同一个 API 调用
【发布时间】:2021-03-23 14:44:08
【问题描述】:

是否可以使用cy.intercept 在同一个测试中多次拦截同一个 API 调用?我尝试了以下方法:

cy.intercept({ pathname: "/url", method: "POST" }).as("call1")
// ... some logic
cy.wait("@call1")

// ... some logic

cy.intercept({ pathname: "/url", method: "POST" }).as("call2")
// ... some logic
cy.wait("@call2")

我希望cy.wait("@call2") 会等待第二次调用 API。但是,第二个cy.wait 将立即继续,因为第一个 API 调用与第二个相同。

【问题讨论】:

    标签: javascript api cypress alias


    【解决方案1】:

    当您设置相同的拦截时,第一个拦截将抓取所有呼叫。但是您可以多次等待第一个别名。

    这是一个(相当)简单的插图

    规格

    Cypress.config('defaultCommandTimeout', 10); // low timeout
                                                 // makes the gets depend on the waits 
                                                 // for success
    
    it('never fires @call2',()=>{
    
      cy.intercept({ pathname: "/posts", method: "POST" }).as("call1")
      cy.intercept({ pathname: "/posts", method: "POST" }).as("call2")
    
      cy.visit('../app/intercept-identical.html')
      
      cy.wait('@call1')                               // call1 fires 
      cy.get('div#1').should('have.text', '201')
    
      cy.wait('@call2')                               // call2 never fires
      cy.wait('@call1')                               // call1 fires a second time
      cy.get('div#2').should('have.text', '201')
    
    })
    

    应用

    <body>
      <div id="1"></div>
      <div id="2"></div>
      <script>
    
        setTimeout(() => {
          fetch('https://jsonplaceholder.typicode.com/posts', {
            method: 'POST',
            body: JSON.stringify({ title: 'foo', body: 'bar', userId: 1 }),
            headers: { 'Content-type': 'application/json; charset=UTF-8' },
          }).then(response => {
            document.getElementById('1').innerText = response.status;
          })
        }, 500)
      
        setTimeout(() => {
          fetch('https://jsonplaceholder.typicode.com/posts', {
            method: 'POST',
            body: JSON.stringify({ title: 'foo', body: 'bar', userId: 2 }),
            headers: { 'Content-type': 'application/json; charset=UTF-8' },
          }).then(response => {
            document.getElementById('2').innerText = response.status;
          })
        }, 1000)
    
      </script>
    </body>
    

    您可以在 Cypress 日志中看到它,

    command call# occurrence (orange tag)
    wait @call1 1
    wait @call2
    wait @call1 2

    【讨论】:

    • 这似乎有效。但是,我觉得cy.wait 实际上并没有等到请求完成,而是会立即进行。仅使用一个 cy.wait 时似乎也会发生这种情况。
    • 很容易反驳你的理论 - 只需注释掉 cy.wait 并看到它失败。如果它不是真的在等待,它仍然会过去。这就是为什么我添加了Cypress.config('defaultCommandTimeout', 10),在构建测试时总是先失败(red-green-refactor)。
    • 这实际上是一个非常好的提示。将超时设置为如此低的数字将强制等待 api 调用。谢谢:)
    【解决方案2】:

    你有很多方法可以做到这一点:

    1. 为拦截的同一端点创建唯一别名

      cy.intercept({ pathname: "/posts", method: "POST" }).as("call")
      
      //First Action
      cy.get("button").click()
      cy.wait("@call").its("request.url").should("contain", "somevalue")
      
      //Second Action
      cy.get("button2").click()
      cy.wait("@call").its("request.url").should("contain", "othervalue")
      
    2. 创建特定的en端点,可以使用glob模式生成动态端点

      //notice path key instead of pathname
      cy.intercept({path: "/post*parameter1=true", method: "POST"}).as("call1")
      
      //First Action
      cy.get("button").click()
      cy.wait("@call1").its("request.url").should("contain", "somevalue")
      
      cy.intercept({path: "/post*parameter2=false", method: "POST"}).as("call2")
      
      //Second Action
      cy.get("button2").click()
      cy.wait("@call2").its("request.url").should("contain", "othervalue")
      
    3. 验证结束时调用的所有端点

       cy.intercept({ pathname: "/posts", method: "POST" }).as("call")
      
       //First Action
       cy.get("button").click()
       cy.wait("@call")
      
       //Second Action
       cy.get("button2").click()
       cy.wait("@call")
      
       //you can add the number of request at the finish of alias
       cy.get("@call.1").its("request.url").should("contain", "somevalue")
       cy.get("@call.2").its("request.url").should("contain", "othervalue")
      
       //another option instead of add the number of request maybe use the position in letters, but I think that it only works for first and last.
       cy.get("@call.first").its("request.url").should("contain", "somevalue")
       cy.get("@call.last").its("request.url").should("contain", "othervalue")
      

    【讨论】:

    • 在#2 中,您不需要虚假参数。赛普拉斯现在允许覆盖,定义的最后一个拦截是第一个匹配的。
    • 不是假参数,只是有查询参数时更具体的与endpoint匹配,没必要使用,但在很多情况下很有用。
    • 重点是,您不需要它 - 可以再次分配相同的 URL。
    【解决方案3】:

    如果您的请求在某些方面有所不同,我会使用aliasing on individual requests。就我而言,我向同一条路线发出了多个发布请求,但正文略有不同。所以我最终做了这样的事情:

    cy.intercept("POST", '/your_route', (req) => {
        if (req.body.hasOwnProperty('your_prop')) {
            req.alias = 'your_alias';
            req.reply(your_response);
        }
    });
    

    这将拦截并存根在正文中具有所需属性的发布请求。

    【讨论】:

      【解决方案4】:
      // wait for 2 calls to complete
      cy.wait('@queryGridInput').wait('@queryGridInput')
      // get
      cy.get("@queryGridInput.all").then((xhrs)=>{});
      

      @alias.all 将等待所有实例完成。 它将返回一个包含所有匹配 @alias 的 xhrs 数组。

      https://www.cypress.io/blog/2019/12/23/asserting-network-calls-from-cypress-tests/

      Cypress: I am trying to intercept the 10 calls which originate from 1 button click but the cy.wait().should is only tapping the last call

      【讨论】:

        猜你喜欢
        • 2023-04-02
        • 2022-11-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-14
        • 1970-01-01
        • 2022-01-14
        • 2016-01-14
        相关资源
        最近更新 更多