Часто бывает так, что веб-приложение состоит из большого количества динамически перестраивающихся форм с разным текстом и элементами управления. Тестирование такого приложения превращается в кошмар.
Нужно прокликать 100500 страниц и проверить весь функционал… И перед следующим релизом еще раз проверить то же самое… И еще… И завтра опять. В какой то момент проверка начинает занимать больше времени, чем разработка нового функционала. «А как же е2е-тесты?» — спросите вы. Но, во-первых, их еще нужно написать. А во-вторых, перед тем как начать их писать, нужно написать тест-кейсы. Очень много тест-кейсов.
Если при чтении этих строк ваш лоб покрылся испариной, не переживайте. В этой статье я поделюсь с вами идеей, как мы в Tinkoff автоматизировали тестирование одного из веб-приложений, не написав при этом ни одного тест-кейса и е2е-теста.
Так уж вышло, что тестирование нашего веб-приложения в основном связано с проверками интерфейса. Нужно проверить, что на экране присутствует кнопка, нужный заголовок и текст, а при вводе невалидного значения в input появляется сообщение об ошибке.
Соответственно, при написании тест-кейса нужно записывать все действия:
и проверки:
После анализа всего функционала нашего веб-приложения мы выделили около 30 уникальных действий и проверок. Стало понятно, что этот процесс можно автоматизировать. Для этого нужно всего лишь отследить все действия тестировщика на странице и реакцию сайта на эти действия.
Начнем с перехвата событий. Чтобы отследить взаимодействие с такими контролами, как кнопка, переключатель и чек-бокс, нужно подписаться на событие click. В каждом фреймворке для этого существуют свои методы. Например, fromEvent в Angular и document.addEventListener в JavaScript и React. Для элементов управления с возможностью ввода, таких как календарь или инпут, изменится только тип события, на которое нужно подписаться: вместо click будет focusout.
Ну и, наконец, самое главное — проверки. То, как сайт должен вести себя в ответ на действия тестировщика.
Что обычно проверяет тестировщик? Например, он ввел невалидное значение в input, сайт отреагировал на это сообщением об ошибке. Или, допустим, мы нажали на кнопку и в ответ открылся новый экран, изменился заголовок, появился новый текст, перестроились элементы управления. Все эти изменения связаны с изменением в DOM-дереве. Есть много вариантов отследить их. Можно, например, использовать MutationObserver в React и JavaScript или ngAfterViewInit в Angular (проставляя директиву на интересующие элементы формы на сайте).
Код будет очень сильно зависеть от верстки. Посмотрим на разметку. Эти кнопки взяты из «Гугл-переводчика».
Несмотря на то что кнопки не представлены в виде тэгов button, присмотревшись к разметке, по css-классу “input-button” можно выделить все кнопки на странице, а по вложенному css-классу “text” можно достать названия кнопок.
Полдела сделано, осталось только записать все, что мы отследили, в тест-кейс.
Мы включаем перехват всех действий на сайте по определенному сочетанию клавиш и только на тестовом контуре. Остановку перехвата всех событий на сайте осуществляем тоже по определенному сочетанию клавиш. Это позволяет начать и остановить автоматическую запись тест-кейса с любого места.
Если посмотреть на автоматически сгенерированный тест-кейс, то это по сути пользовательские сценарии, приведенные к одному виду. А значит, их можно сконвертировать в е2е-тесты. Можно даже сразу писать е2е-тесты после перехвата всех действий и проверок, минуя тест-кейсы.
Сейчас существует большое количество различных фреймворков с gherkin-нотацией, основанных на поведенческих сценариях: SpecFlow, xBehave.net., Cucumber.js, CodeceptJS и т. д.
Чтобы получить features из тест-кейса, нужно добавить перед действиями ключевую фразу When и перед всеми проверками Then и And.
Получим автоматически сгенерированный е2е-тест:
Чтобы прогон тестов заработал, сгенерированных features мало — нужно написать обработчик для всех действий и проверок.
Есть хорошая новость: писать обработчик для каждой фичи не нужно. Как я уже говорила, несмотря на большое количество различных форм на сайте, у нас получилось всего 30 уникальных действий и проверок, а значит, ровно столько же будет и методов в общем обработчике для всех е2е-тестов. Код будет немного отличаться — в зависимости от выбранного фреймворка с gherkin-нотацией и верстки на сайте. Но написание самого обработчика не займет много времени.
Теперь, проверяя очередную задачу, за тестировщика автоматически пишется тест-кейс и прогоняется автоматически сгенерированный е2е-тест.
Если кратко, вам нужно:
Этот подход поможет вам уйти от рутины. Вы сможете автоматизировать написание тест-кейсов и е2е-тестов для простых проверок, связанных с интерфейсом. Мы же пошли еще дальше и проверяем автоматически также запись в БД и отправку в сторонние сервисы.
Об этом, а также о версионировании, стеке технологий и даже о проблемах на первом этапе внедрения и их решении я рассказывала на конференции Heisenbug-2019 в Москве.
В этой статье я постаралась передать основную идею, не вдаваясь в подробности.
Сейчас на написание тест-кейса и е2е-теста у нас уходит в среднем 2 минуты — это в 60 раз быстрее первоначальных подсчетов, когда мы хотели писать тест-кейсы и е2е-тесты вручную.
Мы не меняли процессы в команде. Больше не нужно было выделять емкость тестирования на написание тест-кейсов и брать в команду автоматизатора.
Мы полностью ушли от понятия регресса. Если раньше, при двухнедельном спринте, регресс у нас занимал больше 3 дней, то сейчас регресс занимает время на прогон всех е2е-тестов, а это всего 2 часа.
При ручном написании е2е-тестов очень сложно идти параллельно с тестированием. Теперь же е2е-тесты пишутся автоматически во время тестирования задачи, и тестировщику не нужно проверять один и тот же функционал дважды.
В результате наша команда, не меняя состав, стала работать намного эффективнее.
Нужно прокликать 100500 страниц и проверить весь функционал… И перед следующим релизом еще раз проверить то же самое… И еще… И завтра опять. В какой то момент проверка начинает занимать больше времени, чем разработка нового функционала. «А как же е2е-тесты?» — спросите вы. Но, во-первых, их еще нужно написать. А во-вторых, перед тем как начать их писать, нужно написать тест-кейсы. Очень много тест-кейсов.
Если при чтении этих строк ваш лоб покрылся испариной, не переживайте. В этой статье я поделюсь с вами идеей, как мы в Tinkoff автоматизировали тестирование одного из веб-приложений, не написав при этом ни одного тест-кейса и е2е-теста.
Автоматическое написание тест-кейсов
Так уж вышло, что тестирование нашего веб-приложения в основном связано с проверками интерфейса. Нужно проверить, что на экране присутствует кнопка, нужный заголовок и текст, а при вводе невалидного значения в input появляется сообщение об ошибке.
Соответственно, при написании тест-кейса нужно записывать все действия:
- «Нажали кнопку»
- «Ввели значение ХХХ»
- «Выбрали значение YYY в выпадающем списке»
и проверки:
- «Появился текст: ХХХ»
- «Появилось сообщение об ошибке: YYY»
- «Появился заголовок: ZZZ»
После анализа всего функционала нашего веб-приложения мы выделили около 30 уникальных действий и проверок. Стало понятно, что этот процесс можно автоматизировать. Для этого нужно всего лишь отследить все действия тестировщика на странице и реакцию сайта на эти действия.
Начнем с перехвата событий. Чтобы отследить взаимодействие с такими контролами, как кнопка, переключатель и чек-бокс, нужно подписаться на событие click. В каждом фреймворке для этого существуют свои методы. Например, fromEvent в Angular и document.addEventListener в JavaScript и React. Для элементов управления с возможностью ввода, таких как календарь или инпут, изменится только тип события, на которое нужно подписаться: вместо click будет focusout.
fromEvent(this.elementRef.nativeElement, 'click')
.subscribe(tagName => {
if (tagName === 'BUTTON') {
this.testCaseService.addAction(`Нажать на кнопку "${action.name}"`);
} else if (tagName === 'INPUT-CALENDAR') {
this.testCaseService
.addAction(`Выбрать дату "${action.name}" "${action.value}"`);
}
});
Ну и, наконец, самое главное — проверки. То, как сайт должен вести себя в ответ на действия тестировщика.
Что обычно проверяет тестировщик? Например, он ввел невалидное значение в input, сайт отреагировал на это сообщением об ошибке. Или, допустим, мы нажали на кнопку и в ответ открылся новый экран, изменился заголовок, появился новый текст, перестроились элементы управления. Все эти изменения связаны с изменением в DOM-дереве. Есть много вариантов отследить их. Можно, например, использовать MutationObserver в React и JavaScript или ngAfterViewInit в Angular (проставляя директиву на интересующие элементы формы на сайте).
ngAfterViewInit() {
const tagName = this.nativeElement.nodeName;
const text = this.nativeElement.textContent;
if (['SPAN', 'P'].includes(tagName)) {
this.testCaseService.addContent(`**Появился текст** "${text}"\n`);
} else if (tagName === 'H1') {
this.testCaseService.addContent(`**Появился заголовок** "${text}"\n`);
} …
}
Код будет очень сильно зависеть от верстки. Посмотрим на разметку. Эти кнопки взяты из «Гугл-переводчика».
<div class="tlid-input-button input-button header-button tlid-input-button-text text-icon" role="tab" tabindex="-1">
<div class="text">Текст</div>
</div>
<div class="tlid-input-button input-button header-button tlid-input-button-docs documents-icon" role="tab" tabindex="-1">
<div class="text">Документы</div>
</div>
Несмотря на то что кнопки не представлены в виде тэгов button, присмотревшись к разметке, по css-классу “input-button” можно выделить все кнопки на странице, а по вложенному css-классу “text” можно достать названия кнопок.
Полдела сделано, осталось только записать все, что мы отследили, в тест-кейс.
Мы включаем перехват всех действий на сайте по определенному сочетанию клавиш и только на тестовом контуре. Остановку перехвата всех событий на сайте осуществляем тоже по определенному сочетанию клавиш. Это позволяет начать и остановить автоматическую запись тест-кейса с любого места.
Автоматическое написание е2е-тестов
Если посмотреть на автоматически сгенерированный тест-кейс, то это по сути пользовательские сценарии, приведенные к одному виду. А значит, их можно сконвертировать в е2е-тесты. Можно даже сразу писать е2е-тесты после перехвата всех действий и проверок, минуя тест-кейсы.
Сейчас существует большое количество различных фреймворков с gherkin-нотацией, основанных на поведенческих сценариях: SpecFlow, xBehave.net., Cucumber.js, CodeceptJS и т. д.
Чтобы получить features из тест-кейса, нужно добавить перед действиями ключевую фразу When и перед всеми проверками Then и And.
Получим автоматически сгенерированный е2е-тест:
Feature: Автоматически сгенерированный е2е-тест
Background:
When Авторизуемся "логин" "пароль"
Scenario:
When Нажать на кнопку "Ответил кто-то другой"
Then Переход на экран "Кем приходится клиенту"
And Появился заголовок "Ответил кто-то другой"
When Выбрать в поле "Кем приходится клиенту" "Родственник"
When Выбрать в поле "Уточнение" "Супруг или супруга"
When Нажать на кнопку "Продолжить"
Чтобы прогон тестов заработал, сгенерированных features мало — нужно написать обработчик для всех действий и проверок.
Есть хорошая новость: писать обработчик для каждой фичи не нужно. Как я уже говорила, несмотря на большое количество различных форм на сайте, у нас получилось всего 30 уникальных действий и проверок, а значит, ровно столько же будет и методов в общем обработчике для всех е2е-тестов. Код будет немного отличаться — в зависимости от выбранного фреймворка с gherkin-нотацией и верстки на сайте. Но написание самого обработчика не займет много времени.
When('Нажать на кнопку {string}', async function (button: string) {
const xpath = "//button";
const btn = await getItemByText(xpath, button);
await waitAndClick(btn);
});
When('Выбрать дату {string} {string}', async function (label: string, text: string) {
const xpath = "//*[contains(text(),'" + label + "')]/ancestor::outline";
await inputSendKeys(currentBrowser().element(by.xpath(xpath)), text);
});
Теперь, проверяя очередную задачу, за тестировщика автоматически пишется тест-кейс и прогоняется автоматически сгенерированный е2е-тест.
Если кратко, вам нужно:
- Подписаться на события взаимодействия с элементами управления и реакцию сайта на эти действия (через отслеживание перестроения DOM-дерева).
- Конвертировать данные из п. 1 в е2е-тесты.
- Написать общий обработчик для прогона е2е-тестов.
Этот подход поможет вам уйти от рутины. Вы сможете автоматизировать написание тест-кейсов и е2е-тестов для простых проверок, связанных с интерфейсом. Мы же пошли еще дальше и проверяем автоматически также запись в БД и отправку в сторонние сервисы.
Об этом, а также о версионировании, стеке технологий и даже о проблемах на первом этапе внедрения и их решении я рассказывала на конференции Heisenbug-2019 в Москве.
В этой статье я постаралась передать основную идею, не вдаваясь в подробности.
Заключение
Сейчас на написание тест-кейса и е2е-теста у нас уходит в среднем 2 минуты — это в 60 раз быстрее первоначальных подсчетов, когда мы хотели писать тест-кейсы и е2е-тесты вручную.
Мы не меняли процессы в команде. Больше не нужно было выделять емкость тестирования на написание тест-кейсов и брать в команду автоматизатора.
Мы полностью ушли от понятия регресса. Если раньше, при двухнедельном спринте, регресс у нас занимал больше 3 дней, то сейчас регресс занимает время на прогон всех е2е-тестов, а это всего 2 часа.
При ручном написании е2е-тестов очень сложно идти параллельно с тестированием. Теперь же е2е-тесты пишутся автоматически во время тестирования задачи, и тестировщику не нужно проверять один и тот же функционал дважды.
В результате наша команда, не меняя состав, стала работать намного эффективнее.
NeverIn
Как система понимает, что кликнув тестировщик проверяет заголовок, а не, к примеру, цвет кнопки?
didash Автор
При нажатии на кнопку, в тест-кейс записываются все изменения на сайте, которые мы обрабатываем (в данном примере, будет запись и об изменении цвета кнопки и о появлении заголовка). Если тестировщик посчитает, что какая-то из проверок избыточна в этом кейсе, то он удаляет ее при просмотре тест-кейса. При этом е2е-тест он не трогает, т.к. при прогоне тестов мы «на лету» забираем все тест-кейсы и конвертируем их в е2е-тесты.
PerlPower
А как вы передаете состояние в случае если тест требует перехода со страницы на страницу? Ну в смысле когда весь DOM перезагружается. И в чем именно вы запускаете тесты?
didash Автор
Мы отслеживаем только те изменения, которые проверяет на странице тестировщик. Например, нам интересно появление на новой странице всех блоков с текстом, кнопок, заголовков и т.д. Поэтому, когда перезагружается весь DOM, то мы записываем в тест-кейс только нужные нам изменения через xpath-ы. Для запуска тестов мы используем фреймворк CodeceptJS.