<style>.lazy{display:none}</style>Ожидание в Cypress и как его избежать

Ожидание в Cypress и как его избежать

Перевод статьи «Waiting in Cypress and how to avoid it».

Среди тестировщиков много перфекционистов. Почти у у всех, кого я встречал, возникает чувство дискомфорта, когда они используют команду .wait() в Cypress и останавливают тест на пару секунд. Если это относится и к вам, то вы прекрасно понимаете, что использовать .wait() подобным образом – не самое лучшее решение, и пытаетесь искать альтернативу.

Всякий раз, когда мы используем .wait(), мы хотим, чтобы наше приложение достигло желаемого состояния. Модальное окно закрывается, сетевой ответ возвращается, кнопка меняет состояние и т.д. Cypress был создан с учетом возможности повторных попыток – это означает, что как только команда пройдет, он перейдет к следующей. Если она не проходит, Cypress продолжит повторную попытку через несколько секунд.

Такая архитектура часто приводит к тому, что Cypress слишком быстро перемещается по нашему приложению, и мы хотим заставить его подождать. Затяжное ожидание часто является способом сказать Cypress,что нужно притормозить. Но это не идеальный вариант, как я уже говорил. Поэтому давайте рассмотрим несколько способов, которые можно предпринять, если вы столкнулись с этим неприятным решением.

Содержание:

Подпишитесь на наш ТЕЛЕГРАМ КАНАЛ ПО АВТОМАТИЗАЦИИ ТЕСТИРОВАНИЯ

Использование wаit

В заголовке этой статьи обещано руководство о том, как избежать ожидания, но не будем забегать вперед. Иногда лучшее решение для вас и всей команды — жесткое ожидание. Не тратьте два дня на поиск дополнительных защит, утверждений, перехватов и ограничений, чтобы избежать использования команды .wait(). Эти два дня, вероятно, гораздо превышают общее время ожидания, которое может создать тест. Что еще более важно, ваше время гораздо более ценно, чем время, потраченное на конвейер CI/CD. Вы могли бы заняться чем-то более активным. Перфекционизм стоит дорого. Просто добавьте ожидание и двигайтесь дальше. Неплохо также оставить комментарий “to do”, чтобы каждый, кто столкнется с этим, понял, почему в этом тесте есть ожидание.

  // TODO: ugh, have to use wait. button does not get interactive soon enough
  cy.get('button')
    .wait(2000) 
    .click()

Подсказка от профессионала: Вы можете использовать eslint-plugin-cypress для получения предупреждения о нехватке времени при каждом использовании .wait() в вашем тесте.

Использование “defaultCommandTimeout” для изменения таймаута по умолчанию

Для каждого элемента, который вы запрашиваете с помощью .get() , .contains() или какой-либо другой команды, время ожидания по умолчанию будет составлять 4 секунды. Cypress будет ждать появления элемента в DOM и повторит попытку, пока это возможно. Если 4 секунды недостаточно, можно установить время ожидания глобально для вашего проекта в cypress.json файле, чтобы заставить Cypress ждать дольше:

{
  "defaultCommandTimeout": 5000
}

Установка этого таймаута имеет один важный побочный эффект. Ваши тесты будут завершаться медленнее. Это может продлить цикл обратной связи, поэтому вам, возможно, захочется найти более удобное решение.

Допустим, у вас есть один тест, в котором некоторые элементы загружаются немного медленнее. Вместо глобального применения более длительного тайм-аута вы можете просто применить эту конфигурацию в одном тесте.

it('slow test', { defaultCommandTimeout: 5000 },  () => {

  // will wait 5 seconds for element to appear in dom
  cy.get('slowElement')

})

Этот объект конфигурации работает и для блоков describe:

describe('slow tests', { defaultCommandTimeout: 5000 },  () => {

  it('slow test #1', () => {
    // will apply 5 timeout
  })

  it('slow test #2', () => {
    // here too
  })

})

it('not so slow test', () => {
  // but not here
})

Использование таймаута для каждой команды

Увеличение таймаута для всего теста не всегда является оптимальным способом. Иногда нужно просто подождать, пока появится определенный элемент, а все остальное на странице происходит довольно быстро. Для таких случаев можно воспользоваться объектом options и изменить таймаут для определенной команды.

cy.get('#myElement', { timeout: 10000 })
  .should('be.visible')

Обратите внимание, что мы добавляем timeout в нашу команду .get(), а не в .should(). Интуитивный подход может заключаться в том, чтобы дождаться, пока элемент примет наше утверждение. Но наше утверждение привязано к запросу элемента. Поэтому если утверждение не выполнено, то не выполнится и весь запрос.

Это также может быть полезно, если вы хотите дождаться исчезновения или удаления элемента из DOM, прежде чем перейти к следующему шагу тестирования.

cy.get('#modal', { timeout: 10000 })
  .should('not.exist')

// continue with the test

Примечание: Следует помнить о разнице между not.exist и not.be.visible. Иногда я вижу, как люди путают эти два понятия, и на это есть веские причины. Интуитивно кажется, что это одно и то же. Но если not.exist будет проверять отсутствие элемента в DOM, то not.be.visible пройдет только в том случае, если элемент присутствует в DOM, но он не виден.

Ожидание загрузки страницы

Я уже рассказывал о проверке ссылок в прошлом и о том, почему клики на отдельных ссылках могут быть не лучшим решением. Но если перенаправление страницы является частью тестового потока, вам придется подождать секунду для продолжения теста. Вместо применения команды wait можно использовать тот же принцип, что и в предыдущем примере.

cy.get('#redirectLink')
  .click()

cy.location('pathname', { timeout: 10000 })
  .should('eq', '/about')

Ожидание ответа API

Cypress отлично работает с http-запросами. Если вы ожидаете загрузки некоторых ресурсов в вашем приложении, вы можете перехватить запрос и затем создать для него псевдоним. Этот псевдоним затем будет использоваться с командой .wait(). Проверка будет продолжена только после завершения выполнения этой команды.

cy.intercept('/api/boards').as('boardList')
cy.visit('/')
cy.wait('@boardList')
// continue with test after response happens

Поиск нужного запроса для перехвата – отличный способ убедиться в том, что Cypress будет ждать загрузки страницы со всеми нужными данными.

Если вам нужно дождаться нескольких запросов, вы можете настроить ожидание нескольких псевдонимов с помощью одной команды:

cy.intercept('/api/boards').as('boardList')
cy.intercept('/api/cards').as('cardList')
cy.visit('/')
cy.wait(['@boardList', '@cardList'])
// continue with test after all responses happen

Важное замечание: если вы хотите изменить тайм-аут по умолчанию для ответов API, вам нужно работать с опцией конфигурации responseTimeout.

Ожидание чего угодно

Вы можете дождаться практически чего угодно, передав функцию обратного вызова в команду .should(). Она будет использовать встроенную логику повторных попыток и ждать, пока функция не выполнится. Например, можно подождать, пока все элементы на странице не будут иметь нужный текст. В этом примере показано, как мы можем ждать переупорядочивания списка вместо того, чтобы ждать секунду.

cy.contains('button', 'Sort alphabetically')
  .click()

// list is reordering, it will take a while

cy.get('.list')
  .should( (items) => {

    // but no worries, we will retry until these pass or until timeout
    expect(items).to.have.length(2)
    expect(items[0]).to.have.text('Apples')
    expect(items[1]).to.have.text('Bananas')

  })

// all is good, continue with our test

Это может применяться для чего угодно, например, здесь мы проверяем, имеет ли input правильное значение и класс:

cy.get('input')
  .should( (email) => {

    expect(email).to.have.value('hello@example.com')
    expect(email).to.have.class('valid')

  })

Надеюсь, вам понравилась эта статья.

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *