验证 cypress 中的 Api 调用数量

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

我们在应用程序中使用段,我需要实现端到端测试以验证段调用的数量,我必须确保每个事件仅被调用一次。 我找了好久,找到了这个验证api调用次数的

command

Cypress.Commands.add(`verifyCallCount`, (alias, expectedNumberOfCalls) => {
  const resolvedAlias = alias[0] === `@` ? alias.substring(1) : alias;

  cy.get(`${resolvedAlias}.all`, { timeout: 20000 }).then((calls) => {
    cy.wrap(calls.length).should(`equal`, expectedNumberOfCalls);
  });
});

我在等待api调用后使用这个命令:

cy.wait(`@${eventAlias}`, { timeout: 20000 })
  .then((interception) => {
    return JSON.parse(interception.request.body);
  })
  .then(() => cy.verifyCallCount(eventAlias, 1));

这也是我为 api 调用添加别名的地方:

beforeEach(() => {
  cy.intercept('POST', 'https://api.segment.io/v1', (req) => {
    const body = JSON.parse(req.body);

    if (body.hasOwnProperty('type') && body.type === SampleEvent) {
      req.alias = eventAlias;
    }
  });
});

使用这种方法,当我在本地环境中运行测试时,它通过了,没有任何问题。但同样的测试在 github 的操作上失败了。这是错误:

AssertionError: Timed out retrying after 10000ms: Expected to find element: `eventAlias.all`, but never found it.

我认为

.get()
命令在
.wait()
之后没有被执行,我尝试更改命令的顺序,但没有帮助。

如何在 github actions 中解决这个问题?

还有其他方法可以验证cypress中的api调用次数吗?

感谢您的帮助,谢谢。

cypress github-actions e2e-testing
5个回答
4
投票

您在此处使用的答案验证提出请求的次数是错误的。

const resolvedAlias = alias[0] === '@' ? alias.substring(1) : alias
删除了最初的
@
,但需要保留。

cy.get('${resolvedAlias}.all', { timeout: 20000 })
中的超时也没有效果,它不会等待 20 秒来让所有调用发生。

在您的测试场景中可能有 0、1 或 2 个调用。如果有 0 个调用或 2 个调用,您希望失败,如果恰好有 1 个调用,您希望通过。

如果有 0 次调用,这足以失败

cy.wait(`@${eventAlias}`, { timeout: 20000 })

如果有 2 个调用就会失败,您必须使用硬等待,然后验证调用计数

cy.wait(`@${eventAlias}`, { timeout: 20_000 })

cy.wait(2_000)                   // wait an interval for any extra call to occur

cy.get(`@${eventAlias}.all`)
  .its('length')
  .should(`equal`, 1);           // if two calls happened in interval, fail here

3
投票

您的目标是确保只有 1 次 API 调用。

您将需要测试来等待并查看是否发生第二次呼叫。

it('accurately test that only one API call happens', () => {

  const numOfRequests = 1

  cy.intercept('**/api/*', cy.spy().as('api-spy'))
  
  cy.visit('/');

  cy.wait(1000)
  
  cy.get('@api-spy').its('callCount').should('equal', numOfRequests)

})

我用一个简单的页面进行了测试,故意调用两次,调用之间延迟100ms,

<script>
  fetch('api/1')
  setTimeout(() => fetch('api/2'), 100)  // delayed 2nd fetch we want to test for
</script>

没有经过艰苦的等待,测试就给了我一个错误通过


我也尝试过反转逻辑,但仍然需要等待才能正确测试

cy.intercept('**/api/*', cy.spy().as('api-spy'))
  
cy.visit('/');

cy.wait(1000)
  
cy.get('@api-spy').its('callCount')
  .should('not.equal', 0)
  .and('not.equal', 2)                 // false pass without hard wait
})

在检查body.type的routeHandler内部进行计数

呼叫计数的第二个别名

before(() => {
  cy.wrap(0).as('eventCount')
})

beforeEach(() => {
 cy.intercept('POST', 'https://api.segment.io/v1', (req) => {
      const body = JSON.parse(req.body);

      if (body.hasOwnProperty('type') && body.type === SampleEvent) {
        req.alias = eventAlias;
        cy.get('@eventCount').then(count => {
          cy.wrap(count + 1).as('eventCount')
        })
      }
    });
  });
});

it('checks the count', () => {
  cy.visit('/');
  cy.wait(1000)
  cy.get('@eventCount')
    .should('equal', 1)
})

增加全局

let eventCount = 0;

beforeEach(() => {
 cy.intercept('POST', 'https://api.segment.io/v1', (req) => {
      const body = JSON.parse(req.body);

      if (body.hasOwnProperty('type') && body.type === SampleEvent) {
        req.alias = eventAlias;
        eventCount += 1
      }
    });
  });
});

it('checks the count', () => {
  cy.visit('/');
  cy.wait(1000)
    .then(() => {
      cy.wrap(eventCount)
        .should('equal', 1)
    })
})

2
投票

我注意到你提到了 github 操作。我在 CI 中测试 API 调用时遇到了类似的问题,测试运行速度慢得多并且导致不稳定。

我建议模拟响应,以便从测试中获得更好、更一致的性能。
参考:控制响应

作为奖励,不需要任何长时间的超时,因为你的模拟会立即回复。

beforeEach(() => {
 cy.intercept('POST', 'https://api.segment.io/v1', (req) => {
      const body = JSON.parse(req.body);

      if (body.hasOwnProperty('type') && body.type === SampleEvent) {
        req.alias = eventAlias;

        // here send mock response without any network delay
        req.reply({
          headers: {
            Set-Cookie: 'newUserName=Peter Pan;'
          },
          statusCode: 201,
          body: {
            name: 'Peter Pan'
          }
        })
      }
    });
  });
})

it('tests there is only a single POST from app', () => {

  cy.wait(`@${eventAlias}`)
  cy.wait(100)  
  cy.get(`@${eventAlias}.all`).then((calls) => {
    cy.wrap(calls.length).should(`equal`, 1);
  });
})

1
投票

当您想要获取所有别名调用时,您需要使用

@
来表示别名。因此自定义命令需要更新。

Cypress.Commands.add(`verifyCallCount`, (registeredAlias, expectedNumberOfCalls) => {
  if(alias[0] !== '@') {
   throw new Error ('alias does not start with '@')
  }
  cy.get(`${registeredAlias}.all`, { timeout: 20000 }).then((calls) => {
    cy.wrap(calls.length).should(`equal`, expectedNumberOfCalls);
  });
});

使用方法

cy.intercept('call').as('call')
// some action to trigger call
cy.wait('@call')
// some other actions
cy.verifyCallCount('@call')

-1
投票

还有其他方法可以验证cypress中的api调用次数吗?

这是一种统计 api 调用并等待它们完成的简洁方法。

您可以传递

cy.spy()
作为“响应”,您可以使用它来计算拦截被命中的次数。

在 Cypress 断言中使用

.should()
将等到预期的请求数量返回。

it('test', () => {
  const numOfRequests = 5;
  cy.intercept('https://api.segment.io/v1', cy.spy().as('api-spy'));

  // Do something to trigger 5 requests

  cy.get('@api-spy').its('callCount').should('equal', numOfRequests);
});

如果您正在等待一系列不同的端点,例如

/v1/login
后跟
/v1/getData
等,则
cy.intercept
中的 URL 可能需要使用通配符。

例如:

cy.intercept('https://api.segment.io/v1/**')
© www.soinside.com 2019 - 2024. All rights reserved.