【问题标题】:Is there a way to assert that a route has not been called in Cypress?有没有办法断言赛普拉斯没有调用路由?
【发布时间】:2018-05-01 10:47:24
【问题描述】:

我试图断言在 Cypress 中没有调用路由。我彻底翻阅了文档,但一无所获。

我正在尝试做这样的事情:

cy.get('@myRouteAlias').should('have.not.been.called');

我目前正在通过断言未显示成功的请求 toast 消息来解决此问题,但这是一个脆弱的解决方案。

有什么想法吗?

【问题讨论】:

标签: javascript chai cypress


【解决方案1】:

在 7.6 版中这些都不适合我,但我找到了一个非常简单的解决方案。

假设你有这样的拦截:

cy.intercept('GET', '**/foo/bar**').as('myRequest');

现在你可以这样做了:

cy.wait(2000);
cy.get('@myRequest.all').then((interceptions) => {
    expect(interceptions).to.have.length(0);
});

因此,当请求可能发生时,您等待一段时间,并确保在等待之后它没有发生。对我来说工作得很好,不需要额外的命令。 我在这里找到了解决方案:https://www.gitmemory.com/issue/cypress-io/cypress/15036/780706160

【讨论】:

  • 这不再有效。
  • 这对我有用。 (赛普拉斯 9.2.0)
【解决方案2】:

当我们有路线时:

cy.intercept('PUT', '**/shoes/*', body).as('updateShoes');

以下解决方案对我有用:

cy.get('@updateShoes').then((interception) => {
  assert.isNull(interception)
});

柏树 说: 预期 null 等于 null

当调用“@updateShoes”路由时,(拦截)是一个对象:

{id: "interceptedRequest551", routeId: "1623772693273-2831", request: {…}, state: "Complete", requestWaited: false, …}
id: "interceptedRequest551"
log: {get: ƒ, unset: ƒ, invoke: ƒ, toJSON: ƒ, set: ƒ, …}
request: {headers: {…}, url: "http://localhost:8080/api/shoes/38de4e08", method: "PUT", httpVersion: "1.1", body: {…}}
requestWaited: false
response: {headers: {…}, body: {…}, url: "http://localhost:8080/api/shoes/38de4e08", method: null, httpVersion: null, …}
responseWaited: false
routeId: "1623772693273-2831"
state: "Complete"
subscriptions: []
...}

赛普拉斯抛出错误:

AssertionError
expected { Object (id, routeId, ...) } to equal null

【讨论】:

    【解决方案3】:

    我想我找到了一种符合我预期的方法,使用 cy.interceptcy.state

    1. 通过cy.intercept添加要被嗅探的路由
    2. 稍等片刻,选择您信任的东西
    3. 然后看看你的网址是不是在cy.state('routes')
    it(`should NOT make foo request`, () => {
      // listen for any request with "foo" using cy.intercept
      // I like to return success just to not see warnings in the console...
      cy.intercept(/.foo./, { success: true }).as("fooRequest");
      cy.window().then(win => {
        // do what ever logic could make the request
        makeFooRequestOrSomething();
      });
      // use cy.wait to wiat whatever amount of time you trust that your logoc should have run
      cy.wait(1000);
      /*
       * cy.intercept does not provide any information unless a request is made, so instead
       * we can use the state and make sure our route is not in the list
       */
      let routes = cy.state('routes'); // An object representing all the routes setup via cy.intercept 
      let fooRoutes = [];
      for (let route in routes) {
        // routes[route].requests is an object representing each request
        for (let req in routes[route].requests) {
          let reqUrl = routes[route].requests[req].request.url;
          // test each URL for "foo" and if it has it, add the URL to the array
          if((/foo/).test(reqUrl)) {
            fooRoutes.push(reqUrl);
          }
        }
      };
      // if no request was made to our URL, our array should be empty
      expect(fooRoutes).to.have.property("length", 0);
    });
    
    • routes[route] 可能在某个地方有 alias,如果您想以不同的方式过滤数据,然后查看 routes[route].requests 是否为空,您可以使用它。
    • 我没有在任何地方找到此文档,所以如果有更好的定义可以链接到,请告诉我,尤其是 cy.state 方法。

    【讨论】:

      【解决方案4】:

      cy.route() 弃用后更新cy.intercept()

      如果你使用cy.intercept()cy.state('requests') 将返回未定义alias 的对象,所以我改用xhr.url

      我像这样调整了@SleepWalker 的解决方案:

      commands.js 文件中的命令:

      Cypress.Commands.add('requestsCountByUrl', url =>
          cy.wrap().then(() => {
              const requests = cy.state('requests') || [];
              return requests.filter(req => req.xhr.url === url).length;
          })
      );
      

      在测试中的使用:

      cy.requestsCountByUrl('http://theUrl.com').should('eq', 1);
      

      【讨论】:

        【解决方案5】:

        cy.state 为 0 时似乎未定义。

        此外,如果您想使用@ 调用命令,那么这将起作用。

        Cypress.Commands.add('shouldBeCalled', (alias, timesCalled) => {
          const aliasname = alias.substring(1);
          const requests = cy.state('requests') || [];
        
          expect(
            requests.filter((call) => call.alias === aliasname),
            `${aliasname} should have been called ${timesCalled} times`
          ).to.have.length(timesCalled);
        });
        
        
        cy.shouldBeCalled('@updateCalc', 1);
        

        【讨论】:

          【解决方案6】:

          值得考虑此测试的异步特性,这是前面的示例没有考虑到的。这是一个工作示例:

          cy.route('/my-route').as('myRoute')
          
          const noExpectedCalls = 1
          
          cy.get('@myRoute').then(() => {
            expect(cy.state('requests').filter(r => r.alias === 'myRoute')).to.have.length(noExpectedCalls)
          })
          

          【讨论】:

          • 在执行某些 UI 操作时,是否有办法检查网络选项卡中调用的特定 API?
          【解决方案7】:

          这就是 cypress 团队的做法 (source):

          it("throws when alias is never requested", (done) => {
            Cypress.config("requestTimeout", 100);
          
            cy.on("fail", (err) => {
              expect(err.message).to.include(
                "`cy.wait()` timed out waiting `100ms` for the 1st request to the route: `foo`. No request ever occurred."
              );
          
              done();
            });
          
            cy.server().route(/foo/, {}).as("foo").wait("@foo.request");
          });
          
          

          还有来自相关的docs

          测试失败时触发。从技术上讲,通过绑定到此事件并调用异步完成回调来防止测试实际失败是可能的。但是,强烈建议不要这样做。测试不应该合法地失败。存在此事件是因为它对调试非常有用

          【讨论】:

          • 我在一个客户项目中发现了这个,它破坏了另一个请求(甚至在另一个规范文件中)。
          【解决方案8】:

          这是使用 cypress 的命令断言请求计数的正确方法。

          把它放在你的commands.js 文件中:

          Cypress.Commands.add('requestsCount', (alias) =>
            cy
              .wrap()
              .then(() => cy.state('requests').filter(req => req.alias === alias).length),
          );
          

          比在您的测试中使用如下新命令:

          it('should count requests', () => {
            cy.server();
            cy.route('**').alias('theRequest');
          
            cy.wait('@theRequest');
            cy.requestsCount('theRequest').should('eq', 1);
          });
          

          【讨论】:

          • 这个抽象可能是响应中最好的。
          • 对于任何想知道这是否适用于 cy.intercept 的人,它不适用。
          【解决方案9】:

          不幸的是,以上都没有真正对我有用,我用这个命令得到了它:

          Cypress.Commands.add('shouldBeCalled', (alias, timesCalled) => {
            expect(
              cy.state('requests').filter(call => call.alias === alias),
              `${alias} should have been called ${timesCalled} times`
            ).to.have.length(timesCalled);
          });
          

          然后我像这样使用它:

          // Checks that FetchChatList has not been called
          cy.shouldBeCalled('FetchChatList', 0);
          

          【讨论】:

          • cy.state 未定义?
          • 在执行某些 UI 操作时,是否有办法检查网络选项卡中调用的特定 API?
          【解决方案10】:

          我尝试了 Jonathan 发布的简化版本,但看到 TypeError: Cannot read property 'filter' of undefined and cy.state('requests') is always undefined。

          【讨论】:

          • 奇怪的是,我现在也遇到了这个错误。 @Jennifer Shehane 你有什么想法?
          • 是的,我想我因为没有足够的代表来评论他的回答而被否决:(
          【解决方案11】:

          为了简化@Jennifer Shehane 的最佳答案:

          let requestsCount = (alias) => cy.state('requests').filter(a => a.alias === alias).length;
          
          expect(requestsCount('putRequest')).to.eq(0);
          

          您也可以将其放入赛普拉斯命令文件中!

          【讨论】:

            【解决方案12】:

            作为路由选项中设置的变体onResponse 函数,它会丢弃测试

            例如expect(true).to.be.false;

            如果当前路由发生调用会触发错误

            cy.route({
                url: <url>,
                onResponse: function () {
                   expect("Unexpected Https call").to.be.false;
                }
            })
            

            【讨论】:

            • 这种工作。我必须遵循linked duplicate 中的示例并抛出错误而不是使用断言。断言发生时赛普拉斯没有将测试标记为失败。
            • 这对我不起作用,以一种奇怪的方式。我在我的测试之外放了一个let putRequestMade = false,并在我的路线的onRequest 中放了一个日志语句和一个putRequestMade = true。我在请求之前和之后断言putRequestMade。当我在cy.wait('@putRequest') 之后expect(putRequestMade).to.eq(true) 断言失败时,我可以看到日志语句没有触发。但是,当我删除 putRequestMade 断言时,我看到了日志语句,在调试器中我可以看到 putRequestMade = true。添加一个cy.wait 等待,但如果有断言,它会立即失败!
            • 在执行某些 UI 操作时,是否有办法检查网络选项卡中调用的特定 API?
            【解决方案13】:

            很难测试一个动作没有发生的情况。有了这种断言,你真的只能说:

            “XHR 请求不是在赛普拉斯寻找此 XHR 请求的 400ms 内发出的(或任何你设置的超时时间)”

            这并没有真的确认 XHR 请求从未被调用过。

            话虽如此,赛普拉斯提供了一种方法来检索使用未记录的cy.state('requests') 发出的所有 XHR 请求。您可以检查它的长度,按别名过滤它们等,以确定您想要什么。

            【讨论】:

            • 在 Cypress 6.9.0 中,state 方法似乎不再可用。是不是被别的东西代替了?
            • 在执行某些 UI 操作时,是否有办法检查网络选项卡中调用的特定 API?
            • @Jennifer cy.state(...) is not a function 在 Cypress v7 及以后版本中不再存在。有没有其他功能可以做同样的事情?
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-12-07
            • 1970-01-01
            • 2021-12-22
            • 2021-12-21
            • 1970-01-01
            • 2022-08-02
            相关资源
            最近更新 更多