#javascript #testing #cypress
#javascript #тестирование #cypress
Вопрос:
Контекст
Мне трудно понять, когда разработчик должен обрабатывать асинхронность в Cypress, а когда нет (потому что она обрабатывается под капотом).
Рассмотрим эти 2 теста:
А)
it('stackoverflow example',() => {
cy.get('#soSection').should('contain', 'Stack Overflow').then(() =>{
cy.get('#soButton').click().then(() => {
cy.get('#soSection').should('not.contain', 'Stack Overflow');
});
});
});
Б)
it('stackoverflow example',() => {
cy.get('#soSection').should('contain', 'Stack Overflow');
cy.get('#soButton').click();
cy.get('#soSection').should('not.contain', 'Stack Overflow');
});
- #soSection будет содержать «Переполнение стека», пока мы не нажмем кнопку. Следовательно, обе строки должны выполняться каким-то образом синхронно.
- (A) использует then, который позволяет вам работать с объектом, полученным из предыдущей команды. (B) не использует его.
Что я пробовал до сих пор
Я выполнил оба теста (A) и (B). Оба метода работают хорошо и ведут себя одинаково.
Сомнения
- Если нам нужно было использовать предыдущую тему, тогда мы должны использовать только метод (A) с цепочками. Есть ли какие-либо другие случаи, когда мы ДОЛЖНЫ использовать цепочки, как в примере (A)?
- Посмотрите на 3 строки кода в (B). Мы ожидаем, что эти 3 строки будут выполняться в таком порядке для достижения успеха. Однако Cypress утверждает, что работает асинхронно. Поэтому, как мы можем быть уверены, что эти 3 строки будут выполняться в ожидаемом порядке?
Ответ №1:
Вы должны прочитать страницу под названием Введение в Cypress из официальной документации или, в более общем смысле, все в меню «Основные понятия». Большинство из этих тем и концепций объясняются там.
Команда выдает
Ваше предположение верно, если вам нужна предыдущая тема, которую вам нужно использовать then
, поскольку Cypress является асинхронным и не возвращает тему. Вместо этого он выдает его. Это заявлено как основная концепция управления объектами:
Команды Cypress не возвращают свои объекты, они их выдают. Помните: команды Cypress являются асинхронными и попадают в очередь для выполнения позже. Во время выполнения объекты передаются от одной команды к другой, и между каждой командой выполняется много полезного кода Cypress, чтобы убедиться, что все в порядке.
В разделе «Управление объектами» показаны примеры результатов:
Некоторые методы дают
null
результат и, следовательно, не могут быть связаны, напримерcy.clearCookies()
.Некоторые методы, такие как
cy.get()
orcy.contains()
, выдают элемент DOM, позволяя привязывать к ним дополнительные команды (при условии, что они ожидают объект DOM), например.click()
, или дажеcy.contains()
снова.
Каждая команда выдает что-то свое. Вы можете проверить это в документации для каждой команды. Например, в cy.children
разделе yield указано, что он возвращает элемент DOM.
Технически вам даже не нужно добавлять cy.
перед каждой командой, поскольку все они возвращают cy
, делая каждую команду цепочкой. Обычно вам нужно использовать только .cy
в начале цепочки команд (например, в начале внутри then
блока). Это скорее вопрос стиля кода.
Выполнение команды
Когда вы запускаете 3 cy.get
строки, сами команды не будут выполняться, они просто будут добавлены Cypress в очередь. Cypress будет отслеживать ваши команды и выполнять их по порядку. Каждая команда имеет тайм-аут и будет ожидать выполнения условия команды в течение этого периода времени.
Поскольку команды являются асинхронными состояниями раздела:
Команды Cypress ничего не делают в момент их вызова, а скорее ставят себя в очередь для последующего запуска. Это то, что мы имеем в виду, когда говорим, что команды Cypress являются асинхронными.
Технически я должен привести всю страницу документации, потому что она действительно хорошо структурирована, лаконична и детализирована с большим количеством примеров кода. Я настоятельно рекомендую прочитать это.
Краткие сведения
Короче говоря, с cy.
помощью вызовов вы ставите в очередь команды для запуска Cypress. Если вам нужно запустить свой собственный синхронный код или вам нужна данная тема, вы должны добавить a .then()
после команды Cypress, которую вы хотели бы запустить этот код. Вы могли бы записать все свои последовательные команды в a .then()
, но в этом нет необходимости, вы просто воссоздаете then
рождественскую елку гибели JS, известную как ад обратного вызова. Так что используйте только then
в том случае, если вам нужен данный объект предыдущей команды или вы хотите ввести синхронный код. Конечно, после некоторого синхронного кода (например if
, условия) вам нужно снова вызвать cy.
, и они будут поставлены в очередь внутри этого then
. Добавляете ли вы оставшиеся cy.
команды внутри then
или на один или несколько уровней выше, зависит от того, нужно ли вам работать над тем, что происходит внутри данного then
блока, или как вы предпочитаете стилизовать свой код. Вот пример из документов, измененных для демонстрации:
it('test', () => {
cy.visit('https://app.com')
// these lines will be queued up and will be run by Cypress in order
cy.get('ul>li').eq(4)
cy.get('.nav').contains('About')
// we want to use the .user field's value
cy.get('.user')
.then(($el) => {
// this line evaluates after the .then() executes
let username = $el.text()
// synchronous code to decide which commands to run (queue up)
if (username) {
// "queue up" other commands
cy.contains(username).click()
cy.get('ul>li').eq(2)
} else {
cy.get('My Profile').click()
}
})
// command that doesn't depend on data inside the `then` block
cy.get('.status').contains('Done')
})