Я реализовал кэширование данных API в своем приложении, чтобы, если данные уже присутствуют, они не извлекались повторно.
Я могу перехватить начальную выборку
cy.intercept('**/api/things').as('api');
cy.visit('/things')
cy.wait('@api') // passes
Чтобы проверить, работает ли кеш, я хотел бы явно проверить обратное.
Как я могу изменить поведение cy.wait()
аналогично тому, как .should('not.exist')
изменяет cy.get()
, чтобы разрешить отрицательную логику?
// data is cached from first route, how do I assert no call occurs?
cy.visit('/things2')
cy.wait('@api')
.should('not.have.been.called') // fails with "no calls were made"
Минимальный воспроизводимый пример
<body>
<script>
setTimeout(() =>
fetch('https://jsonplaceholder.typicode.com/todos/1')
}, 300)
</script>
</body>
Поскольку мы проверяем отрицательный результат, полезно сначала сделать так, чтобы тест провалился. Отправьте приведенный выше HTML-код и используйте его, чтобы подтвердить, что тест не пройден, затем удалите fetch()
, и тест должен пройти.
Дополнительный пакет cypress-if может изменить поведение команды по умолчанию.
cy.get(selector)
.if ('exist').log('exists')
.else().log('does.not.exist')
Предположим, что ваши вызовы API выполняются в течение 1 секунды после действия, которое их вызовет — cy.visit()
.
cy.visit('/things2')
cy.wait('@alias', {timeout:1100})
.if (result => {
expect(result.name).to.eq('CypressError') // confirm error was thrown
})
Вам нужно будет перезаписать команду cy.wait()
, чтобы проверить цепочку команд .if ()
.
Cypress.Commands.overwrite('wait', (waitFn, subject, selector, options) => {
// Standard behavior for numeric waits
if (typeof selector === 'number') {
return waitFn(subject, selector, options)
}
// Modified alias wait with following if ()
if (cy.state('current').attributes.next?.attributes.name === 'if') {
return waitFn(subject, selector, options).then((pass) => pass, (fail) => fail)
}
// Standard alias wait
return waitFn(subject, selector, options)
})
Пока только cy.get()
и cy.contains()
перезаписываются по умолчанию.
Если синтаксис if ()
кажется вам неправильным, ту же логику можно использовать в пользовательской команде.
Cypress.Commands.add('maybeWaitAlias', (selector, options) => {
const waitFn = Cypress.Commands._commands.wait.fn
// waitFn returns a Promise
// which Cypress resolves to the `pass` or `fail` values
// depending on which callback is invoked
return waitFn(cy.currentSubject(), selector, options)
.then((pass) => pass, (fail) => fail)
// by returning the `pass` or `fail` value
// we are stopping the "normal" test failure mechanism
// and allowing downstream commands to deal with the outcome
})
cy.visit('/things2')
cy.maybeWaitAlias('@alias', {timeout:1000})
.should(result => {
expect(result.name).to.eq('CypressError') // confirm error was thrown
})
Изящный маленький трюк, который я узнал из курса Сети Глеба.
Вы захотите использовать cy.spy()
с вашим перехватом и использовать cy.get()
для псевдонима, чтобы иметь возможность проверить, что звонки не были сделаны.
// initial fetch
cy.intercept('**/api/things').as('api');
cy.visit('/things')
cy.wait('@api')
cy.intercept('METHOD', '**/api/things', cy.spy().as('apiNotCalled'))
// trigger the fetch again but will not send since data is cached
cy.get('@apiNotCalled').should('not.been.called')
Это общий ответ. Без воспроизводимого примера это то, что я могу предоставить.
Я также пробовал cy.spy()
, но с жестким cy.wait()
, чтобы избежать задержки в приложении после изменения маршрута.
const spy = cy.spy()
cy.intercept('**/api/things', spy)
cy.visit('/things2')
cy.wait(2000)
.then(() => expect(spy).not.to.have.been.called)
Запуск теста на сжигание из 100 итераций, это кажется нормальным, но при таком подходе все еще есть шанс ненадежного теста, IMO.
Лучшим способом было бы рекурсивно опросить шпиона:
const spy = cy.spy()
cy.intercept('**/api/things', spy)
cy.visit('/things2')
const waitForSpy = (spy, options, start = Date.now()) => {
const {timeout, interval = 30} = options;
if (spy.callCount > 0) {
return cy.wrap(spy.lastCall)
}
if ((Date.now() - start) > timeout) {
return cy.wrap(null)
}
return cy.wait(interval, {log:false})
.then(() => waitForSpy(spy, {timeout, interval}, start))
}
waitForSpy(spy, {timeout:2000})
.should('eq', null)
Извините, у меня это не работает - я получаю ложноположительный результат, когда тестирую красный / зеленый цвет.