Java e2e что такое
Перейти к содержимому

Java e2e что такое

  • автор:

Руководство по сквозному тестированию: что такое E2E-тестирование с примерами

Сквозное тестирование (End-to-end, E2E, Chain testing) — это вид тестирования, используемый для проверки программного обеспечения от начала до конца, а также его интеграцию с внешними интерфейсами. Цель сквозного тестирования состоит в проверке всего программного обеспечения на предмет зависимостей, целостности данных и связи с другими системами, интерфейсами и базами данных для проверки успешного выполнения полного производственного сценария.

Наряду с программной системой тестирование также обеспечивает проверку пакетной обработки и обработки данных из других вышестоящих и нижестоящих систем. Отсюда и название «End-to-End». Сквозное тестирование обычно проводится после функционального и системного тестирования. Для его проведения используются реальные данные и тестовая среда для имитации рабочего режима.

Зачем нужно сквозное тестирование?

Сквозное тестирование проверяет весь системный флоу и повышает уверенность за счет своевременного обнаружения проблем и увеличения покрытия тестами подсистем. Современные системы ПО сложны и взаимосвязаны с большим количеством подсистем, которые могут существенно отличаться от существующих систем. Вся система может разрушиться из-за отказа любой подсистемы, что представляет собой серьезный риск. Этого риска мы как раз и стремимся избежать с помощью сквозного тестирования.

Процесс сквозного тестирования:

На схеме ниже представлен обзор процесса сквозного тестирования.

Основные виды деятельности, связанные со сквозным тестированием:

  • Изучение требований к сквозному тестированию;
  • Настройка тестовой среды и требования к оборудованию/программному обеспечению;
  • Описание всех процессов системы и ее подсистем;
  • Описание ролей и ответственности для всех систем;
  • Методология и стандарты тестирования;
  • Сквозное отслеживание требований и разработка тест-кейсов;
  • Входные и выходные данные для каждой системы.

Как писать тест-кейсы для сквозного тестирования?

Фреймворк сквозного тестирования включает в себя три части:

  1. Создание пользовательских функций
  2. Создание условий
  3. Создание тест-кейсов

Рассмотрим каждую из них подробно.

Создание пользовательских функций

Как часть построения пользовательских функций, должны быть выполнены следующие действия:

  • Перечислить функции системы и их взаимосвязанные компоненты;
  • Перечислить входные данные, действия и выходные данные для каждой характеристики или функции;
  • Определить отношения между функциями;
  • Определить, является ли функция многократно используемой или независимой.

Например, рассмотрите сценарий, при котором вы входите в свой банковский аккаунт и переводите деньги со своего счета на счет в другой банк (сторонняя подсистема):

  1. Войти в банковскую систему.
  2. Проверить сумму остатка на счете.
  3. Перевести определенную сумму со своего счета на другой банковский счет (сторонняя подсистема).
  4. Проверить текущий баланс счета.
  5. Выйти из приложения.
Построение условий на основе пользовательских функций

В рамках построения условий выполняются следующие действия:

  • Построение набора условий для каждой определенной пользовательской функции;
  • Условия включают последовательность, время и условия данных.

Например, проверка дополнительных условий, таких как:

Страница авторизации

  • Неверное имя пользователя и пароль
  • Проверка с действительным именем пользователя и паролем
  • Проверка надежности пароля
  • Проверка сообщений об ошибках

Сумма остатка

  • Проверьте текущий баланс через 24 часа (Если перевод отправляется в другой банк)
  • Проверьте сообщение об ошибке, если сумма перевода больше суммы текущего баланса.

Создайте тестовый сценарий

Построение тестового сценария для определенной пользовательской функции

  • Войти в систему
  • Проверить сумму остатка на банковском счете
  • Перевести сумму остатка на банковском счете

Создание нескольких тест-кейсов

Создайте один или несколько тест-кейсов для каждого определенного сценария. Тест-кейсы могут включать каждое условие как отдельный тестовый пример.

Инструмент сквозного тестирования

testRigor

В мире сквозного тестирования лидером отрасли является testRigor. Он помогает создавать тесты без кода для веб-интерфейса, нативных и гибридных мобильных приложений, мобильных браузеров и API. С его помощью можно тестировать электронную почту и SMS, загруженные файлы .XLS, .DOC, .PDF и т. д.

Функции:

  • Написание тестов без кода просто на английском языке.
  • Покрытие Web + Mobile + API в одном тесте. Кроссплатформенная и кроссбраузерная поддержка.
  • Создание тестов в 15 раз быстрее по сравнению с Selenium.
  • Сокращение обслуживания тестов до 99,5%.
  • testRigor безопасен и соответствует стандарту SOC 2 Type 2.
  • Интеграция с CI/CD и управлением тест-кейсами.
  • Выполнение 1000 тестов и получение результатов менее чем за 30 минут.

Метрики сквозного тестирования:

Ниже приведены некоторые метрики, используемые для оценки прогресса сквозного тестирования.

  • Статус подготовки тест-кейса: показывает реальный прогресс подготовки тест-кейса по сравнению с запланированным.
  • Еженедельный прогресс тестирования. Дает подробную информацию о завершении теста за неделю в процентах. Неудачные, не выполненные и выполненные тесты по сравнению с запланированными для выполнения тестами.
  • Статус и детали дефектов — показывает процент открытых и закрытых дефектов понедельно. Кроме того, распределение дефектов по неделям в зависимости от серьезности и приоритета.
  • Доступность среды — общее количество часов доступности / общее количество часов, запланированных в день для тестирования.

Сквозное тестирование vs системное тестирование

Сквозное тестирование

Системное тестирование

Проверяет программную систему, а также взаимосвязанные подсистемы.

Проверяет только программную систему в соответствии со спецификациями требований.

Проверяет весь сквозной поток процессов.

Проверяет функциональные возможности и функции системы.

Для тестирования рассматриваются все интерфейсы и серверные системы.

Рассматриваются функциональное и нефункциональное тестирование

Выполняется после завершения тестирования системы.

Сквозное тестирование включает в себя проверку внешних интерфейсов, которую сложно автоматизировать. Следовательно, ручное тестирование предпочтительнее.

Для тестирования системы можно выполнять как ручное, так и автоматизированное тестирование.

В заключение

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

Приглашаем всех желающих на открытое занятие, на котором мы познакомимся с фреймворком Selenide и перепишем существующие тесты на него. Регистрация доступна по ссылке.

  • java qa
  • end-to-end testing
  • сквозное тестирование
  • E2E тестирование
  • selenide
  • Блог компании OTUS
  • Тестирование веб-сервисов

Создаем читабельный e2e тест для микросервисов на Spring Boot с помощью Cucumber 7 и Wiremock

Дорогой читатель, это мой первый туториал и если я что‑то упустил или не объяснил, хотя стоило бы, напиши пожалуйста комментарий и я обновлю статью.

TL;DR

Рекомендую быстро пробежаться по коду перед прочтением статьи т к в самой статье я объясню только неочевидные моменты реализации e2e тестирования.

Проблема

Нужно организовать сквозное/e2e (end-to-end) тестирование (далее буду использовать термин e2e) приложения, которое состоит из нескольких микросервисов, микросервисы используют сторонние апи.

Знакомимся c проектом

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

Аккаунт можно создать только когда тебе есть 18 лет.

А в постах нельзя использовать матные слова. Если найдено матное слово — пост не будет создан.

Разные детали про проект

  • в проекте используется монорепозиторий
  • технологии: maven, Spring Boot, Hibernate, H2, Feign client (из Spring Cloud)
  • в целях упрощения туториала не используем никаких либ для миграции базы (liquibase, flyway) и создаем таблицы с помощью Hibernate
  • в целях упрощения в качестве базы используем in-memory базу H2
  • для примера используется приложение с двуми микросервисами, но их может быть и больше
  • e2e тесты лежат в отдельном модуле с названием e2e-tests
  • для запуска сервисов для e2e тестирования будем использовать Spring профиль с название ‘e2e’

Про сервисы и их обязанности

У нашей социальной сети есть два микросервиса и эти микросервисы пользуются сторонними апи. Ниже диаграмма с описанием приложения.

UserService — отвечает за все, что связано с пользователем.
PostService — отвечает за все, что связано с постами.
Age API — сторонний сервис, который помогает нашей соцсети узнать реальный возраст пользователя.
Word API — сервис, который помогает понять есть ли в тексте матные слова.

Краткое описание технологий для e2e тестирования

Для создания e2e тестов будут использованы следующие либы: JUnit 5, Wiremock 2, Cucumber 7, Awaitility 4.

Wiremock — для мокирования(имитации) http ответов. В нашем случае используем wiremock для мокирования ответов от сторонних API. Короткий пример (создаем мок для get запроса на получение пользователя с id 1):

wiremockServer.stubFor(get("/api/users/1").willReturn(aResponse() .withStatus(200) .withHeader("Content-Type", "application/json") .withBody(jsonHelper.fromObjectToString(user))) );

Cucumber — для создания удобного для чтения e2e теста. Короткий пример:

Функция: Снятие денег со счета Сценарий: Успешное снятие денег со счета Дано на счете пользователя имеется 1200 рублей Когда пользователь снимает со счета 200 рублей Тогда на счете пользователя имеется 1000 рублей

Awaitility — для создания проверок в нашем e2e тесте с условиями, которые могут выполниться не сразу, а через какое-то время. Короткий пример:

// сделали что-то с кастомером someCustomerService.doSomething(); // тут мы ждем 5 секунд и проверяем обновился ли статус кастомера await().atMost(5, SECONDS).until(customerStatusIsUpdated()); // или могли бы проверять обновился ли статус кастомера каждые // 2 секунды в течение 10 секунд Awaitility.await().atMost(10, TimeUnit.SECONDS) .pollInterval(2, TimeUnit.SECONDS) .until(customerStatusIsUpdated());

Проговариваем логику e2e теста

Цель протестировать весь жизненный цикл пользователя в нашей социальной сети.

Напомню, что в соцсети нельзя создавать акки, если тебе меньше 18 лет и нельзя использовать маты в постах.

Логика теста: пользователь создает аккаунт в соцсети (попробуем создать аккаунт для двух людей: одному есть 18 лет, другому нет) и ведет какую-то активность (создадим пару постов с матами и без) и в итоге решает удалить свой аккаунт.

Что нужно сделать для успешного выполнения e2e теста

Чтобы провести e2e тестирование нам нужно замокать все сторонние сервисы, в этом нам поможет Wiremock. Наши сервисы будут работать и ничего не подозревать о том, что сторонние API не настоящие. На диаграмме ниже можно увидеть, что сервисы соцсети обращаются к wiremock, а не к реальным апи.

Подготавливаем сервисы для e2e тестирования

Для ускорения выполнения e2e тестов можно использовать in-memory базы данных. В нашем примере мы так и делаем — используем H2 базу данных вместо, например, MySql.

Так же нужно чтобы http клиенты, которые используются для общения со сторонними API, можно было перенаправить на Wiremock (то есть нужно изменить хост и порт для сторонних API). При этом хотелось бы чтобы использовался тот же самый клиент что и для прода. В нашем примере это сделано через спринговые проперти.

В проекте нашей социальной сети для создания клиентов используется FeignClient (из Spring Cloud), но и для других либ для создания клиентов логика будет такая же.

Проперти в аппликейшен файле для e2e профиля с указанием хоста и порта Wiremock-a:

Читаем готовый e2e тест и разбираемся как писать свои

Наша цель создать легкочитаемый e2e тесты. Для этого мы используем либу Cucumber. А читабельное описание нашего теста будет храниться в файле с расширением .feature

Ниже готовый файл с шагами для нашего e2e теста:

Наш файл .feature содержит ключевые слова:
“Функция” — тут обычно пишут описание тестируемого функционала. В нашем случае у нас один тест, который тестит вообще всё приложение, поэтому я использовал подходящее описание.
“Сценарий” — название и краткое описание нашего e2e теста.
“Дано”, “Когда”, “Тогда” — это ключевые слова, которые мы используем для описания шагов теста.
Так же доступны такие ключевые слова: Допустим, Если, Затем, И, Иначе, Ктомуже, Но, Пусть, Также, То.

Напомню, что все эти ключевые слова это синтаксический сахар.

Плагин для файлов .feature

Для удобного редактирования файлов типа .feature нужно установить плагин. IntelliJ Idea подскажет установить его когда вы откроете файл типа .feature

Пишем в фича файле на русском языке

Название шагов на русском можно писать без каких-либо дополнительных настроек. А чтобы можно было использовать русские ключевые слова нужно в начале .feature файла добавить это:

# language: ru

Пример можно увидеть на скриншоте выше.

Создаем новый шаг в фича файле

Шаг, для которого еще нет кода, который будет выполняться, подсвечивается желтым цветом:

Если навести курсов на этот шаг или прожать Alt+Enter когда курсор стоит на этой строке, то можно увидеть такое меню

Тут идея предлагает создать так называем step definition или метод, который будет содержать код, который будет выполнен для этого шага во время теста.

Если кликнуть на ”Create step definition”, то появится такое меню

Я выбрал существующий файл ThirdPartyServicesStepsDefinitions, если у вас таких пока нет, то выбирайте первый вариант.

Для меня в классе был создан метод с аннотацией @Дано, а также название метода на русском (если вдруг не знали, то можно писать на java используя русский язык ��). В этом методе нужно будет поместить код для этого шага

Давайте посмотрим на то, что я положил в этот метод. Чтобы произошло то, что написано в названии этого шага, в коде мы должны замокать ответ от Age Api. Вот как это сделано:

Тут создается мок для POST запроса по урлу /is-person-adult а также добавлено условие, что в теле запроса должен быть определенный джисон.

Проверяем с помощью Awaitility условие, которое выполнится не сразу

Давайте так же рассмотрим шаг, где используется библиотека Awaitility. Напомню, что Awaitility нам нужен в случае, если нужно проверить условие, которые выполнится не сразу, а через какое-то время.

В нашем e2e тесте Awaitility используется на шаге проверки того, что у пользователя больше нет постов в соцсети. Тут нам нужен Awaitility т к в нашей соцсети удаление пользователя и его постов занимает много времени.

В этом методе мы запускаем проверку того, что у пользователя больше нету постов. Каждые 2 секунды на протяжении 10 секунд Awaitility будет выполнять код из метода .until()

Если в течение 10 секунд лямбда из .until() не вернет true, то вылетит эксепшен и тест завалится.

Реализацию остальных шагов в нашем .feature файле я объяснять не буду т к они содержат либо простой код, либо код похожий на тот, что я объяснил выше.

Смотрим вспомогательный код для e2e теста

Код для реализации логики в шагах из фича файла:

  • в пакете src/main/java/org/example можно увидеть класс c кодом для запуска Spring Boot приложения (в нем выключены сервлеты чтобы запускался только spring контекст). Этот класс нужен нам т к без него не получится запустить e2e тест со spring контекстом, и значит не получится удобно и красиво инжектировать бины в классы.
  • в пакете src/test/java/org/example/common/client у нас клиенты для сервисов для проведения CRUD операций для постов и пользователей.
  • src/test/java/org/example/common/config — пакет с конфигами, про них чуть ниже.
  • класс JsonHelper это обертка для ObjectMapper для работы с джисонами.
  • класс TestContext это класс для удобного хранения и передачи данных между шагами теста, который в сути своей обычная мапа.
  • класс AbstractStepsDefinitions — содержит код, который будет использован всеми остальными StepsDefinitions классами

Конфиги для запуска e2e теста

  • BeanConfig — тут ничего особенного, он содержит методы для создания spring бинов (в этом классе мы создаем инстанс Wiremock сервера)
  • CucumberSpringConfiguration — класс для конфигурации application context (в нашем случае это Spring контекст) который будет подниматься при запуске .feature файлов.
  • EndToEndCucumberTestConfiguration — в этом классе содержатся настройки для запуска .feature файлов с помощью JUnit 5. Давайте пройдемся по аннотациям из этого класса т к они могу ввести в замешательство.

@Suite — это аннотация из JUnit 5 для создания класса-запускатора тестов

@IncludeEngines(«cucumber») — эта аннотация для добавления Cucumber engine (движка для запуска тестов созданных с использованием Cucumber).

@ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = «org.example») — тут мы указываем местоположение джава классов со StepsDefinitions и также в пакете должен быть класс с аннотацией @CucumberContextConfiguration иначе будет ошибка

@ConfigurationParameter(key = FEATURES_PROPERTY_NAME, value = «src/test/resources/features/») — тут указываем путь к пакету с .feature файлами

Запуск сервисов и e2e теста

Запускаем сервисы с e2e профилем. В корне модуля для PostService (/social-network-java-spring-wiremock-cucumber-e2e-test/post-service) запускаем:

mvn spring-boot:run '-Dspring-boot.run.profiles=e2e'

Таким же образом запускаем UserService.

Запускаем тесты из консоли. Переходим в модул с e2e тестами (/social-network-java-spring-wiremock-cucumber-e2e-test/e2e-tests) и запускаем:

mvn clean install

Если у вас появились какие-то вопросы, пишите, отвечу или обновлю пост.

End-to-End Testing

End-to-end (often referred to as E2E) testing is a testing methodology used to test a complete flow from the beginning to the end of one or more specific use cases within your application. Its main goal is to test your full application from an end user’s perspective and simulate everything that a real user would do. This includes typing stuff, clicking buttons and/or links and test how your application responds to this behaviour.

End-to-end testing can avoid, if done correctly, many complex situations and ensures that your application is stable and keeps working how it is intended to work. If a button suddenly does not respond or does not behave in a way that you expect it to behave, end-to-end testing can detect this and warn you that your application is not functioning correctly. Thanks to this, you can drastically improve stability, which gives you more time to implement features and reduces development costs.

End-to-end testing often starts with a user interacting with a user interface which then interacts with your backend that executes code behind-the-scenes in order to display the correct information on the user interface. This is done through an automated browser where the end-to-end test clicks and types for you.

Selenium

Selenium is an end-to-end testing framework which is available in many popular programming languages including Java, Python, JavaScript, … . It provides a set of tools that allows you to connect with an automated browser that interacts with your frontend.

Some of Selenium’s features are:

  • Cross-platform testing (PC, Android, iPhone, …);
  • Locating web elements on a page with advanced filtering such as XPath;
  • Selecting and interacting with found elements;
  • Capture screenshots at critical moments such as adding an entry in a table, submitting a form, …;
  • Keyboard & mouse emulation;

Selenium offers other products such as Selenium IDE and Selenium Grid, but at the core of Selenium, there is WebDriver. WebDriver is an API provided by Selenium which controls the automated browser and is used to fluently write your test code. Each browser (Firefox, Chrome, …) has its own implementation of WebDriver. WebDriver then interacts with your browser’s driver in order to execute the behaviour that you have specified in your test.

Practical example

This post comes with a repository (branch selenium ) with a basic example of how Selenium works in a Spring Boot application. This explains how you can use the WebDriver API to fetch and interact with certain elements and do various assertions or interactions to write your tests. It also includes a small example of WebDriverWait , which allows to pause the test and wait until a specific condition has been met on the page (i.e. loading an image, adding an entry to a table, …). Both E2E tests capture a screenshot during interaction with the frontend and those screenshots can be found in the target folder. You can check in the implementation ( ScreenshotService ) on how easy it is to add screenshot support. This can be combined with JUnit functionality (Rule for JUnit 4 and Extension for JUnit 5), for example when you can take a screenshot if your test ends or fails.

For simplicity, I chose to use the Firefox browser. You will need the browser installed (or change the WebDriver implementation to your browser) in order for the test to work. You can run the tests yourself and watch how Selenium interacts with the automated browser. Make sure you changed the path of the geckodriver binary in BaseSeleniumE2ETest .

test_E2E_AssertVisibleHtmlElements()

The first E2E test is a basic test that covers all the HTML elements that are visible (or, should be visible) on the main page. In this test, various methods and approaches are used to find the desired elements and then to assert attributes like visiblity, text, colors, … . Notice how the WebDriverWait pauses the test and waits for the image to load before proceeding. By far the best approach to find elements on the page is to use XPath, which is a language that provides an easy way to find HTML elements on the page. //table[@class=’table’] means that we want to find a table element on the page that contains the class table .

 @Test void test_E2E_AssertVisibleHtmlElements()  // Find the content of the title by going through various HTML attributes on the page // You can go step-wise through your HTML elements to find the right one by chaining findElement() var title = driver .findElement(By.id("header")) .findElement(By.className("navbar")) .findElement(By.tagName("a")).getText(); assertThat(driver.getTitle()).isEqualTo(APP_TITLE); assertThat(title).isEqualTo(APP_TITLE); // Find the CSS value of a HTML element var navColor = driver .findElement(By.id("header")) .findElement(By.className("navbar")).getCssValue("background-color"); var navColorHex = Color.fromString(navColor).asHex(); assertThat(navColorHex).isEqualTo(COLOR_HEX_ORANGE); // We can ask the WebDriverWait to wait until the logo is done loading so we can proceed with the test // No chaining needed here, because 'logo' is only used by the img tag waitDriver.until(ExpectedConditions.elementToBeClickable(By.className("logo"))); // Find the image through the class name 'logo' var image = driver.findElement(By.className("logo")); assertThat(image.getTagName()).isEqualTo("img"); assertThat(image.isDisplayed()).isTrue(); // Easily find the table and the button on the page through XPath var thead = driver.findElement(By.xpath("//table[@class='table']/thead")); var theadRows = thead.findElements(By.tagName("th")); assertThat(theadRows.size()).isEqualTo(2); assertThat(theadRows.get(0).getText()).isEqualTo("Description"); assertThat(theadRows.get(1).getText()).isEqualTo("Delete"); var tbody = getTableBody(); assertThat(tbody.findElements(By.tagName("tr")).size()).isZero(); var addButton = getAddTodoButton(); assertThat(addButton.getText()).isEqualTo(ADD_NEW_TODO_BUTTON); captureScreenshot(); > 

test_E2E_addNewTodoToTable_And_DeleteTodoFromTable()

The second (and last) E2E test actually interacts with the browser and asserts various elements based on these interactions. The test is responsible for clicking on the “Add” button, fill in an item in the description text box and confirming the item, adding it to the table of TODOs. Right after adding the item to the table, the test assures that the table contains an item and has the correct content, then deletes the item from the table. Again, we use XPath in order to find the correct element because of the simple approach. This time, the syntax is a little bit more advanced but definitely still easy to use. //div[@class=’modal-body’]/form/button[@type=’submit’] searches for a button of type submit in a form in a div with class modal-body . We can find the first element in the table by using //td[1] . Notice how it says [1] and not [0] . The starting index in XPath is always 1. The “Delete” button is found by searching for the text inside the button ( //button[@type=’submit’ and text() = ‘Delete’] ).

 @Test void test_E2E_addNewTodoToTable_And_DeleteTodoFromTable() throws InterruptedException  var addButton = getAddTodoButton(); // interact with the button addButton.click(); // Wait two seconds to show visible changes Thread.sleep(2000); // Find an element on the page by using its id in the HTML element var formTitle = driver.findElement(By.id("modalTitle")); assertThat(formTitle.getText()).isEqualTo(MODAL_TITLE); var descriptionBox = driver.findElement(By.id("todoDescription")); assertThat(descriptionBox.getTagName()).isEqualTo("input"); // Type "this is an E2E test" in the input field descriptionBox.sendKeys("This is an E2E test"); // Wait two seconds to show visible changes Thread.sleep(2000); var confirmButton = driver.findElement(By.xpath("//div[@class='modal-body']/form/button[@type='submit']")); // Interact with the "Add todo" button confirmButton.click(); captureScreenshot(); var tbody = getTableBody(); var rows = tbody.findElements(By.tagName("tr")); assertThat(rows.size()).isEqualTo(1); // Use CSS selectors to find the columns of the TODO entry on the page assertThat(tbody.findElement(By.cssSelector("tr:nth-child(1)")) .findElements(By.tagName("td")).size()).isEqualTo(2); assertThat(rows.get(0).findElement(By.xpath("//td[1]")).getText()).isEqualTo("This is an E2E test"); // Wait two seconds to show visible changes Thread.sleep(2000); // Find the delete button based on the input type and the text in the button var deleteButton = tbody.findElement(By.xpath("//button[@type='submit' and text() = 'Delete']")); deleteButton.click(); // Verify that the item is removed from the table by checking if there are no elements assertThat(getTableBody().findElements(By.tagName("tr")).size()).isEqualTo(0); > 

Share on LinkedIn Share on Twitter Share on Facebook

Yolan Vloeberghs is a Java and Cloud engineer with a sharpened focus on all things related to cloud, specifically AWS. He loves to play around with various technologies and frameworks and is very passionated and eager to learn about everything related to cloud-native development.

Home

Недавно я разговаривал с моим непосредственным руководителем про end-to-end (e2e) тесты и их предназначение. Возможно, дело в моем лингвистическом прошлом, но я лучше думаю, Когда пишу. Я решил перечислить, чем должен быть хороший e2e-тест и что он должен делать, а чего не должен. Так я и пришел к этому списку.

Кстати говоря, список этот и не задумывался исчерпывающим – он будет меняться и развиваться вместе с изменениями и развитием моего тестирования и понимания тестирования, поэтому смело делитесь своими предложениями про e2e-тесты.

Что должен делать хороший end-to-end тест?

Не буду углубляться в словарные определения e2e-тестов – думаю, любой в состоянии их нагуглить. Меня больше интересуют те характеристики e2e-тестов, которые я считаю ценными.

Он должен имитировать валидный сценарий, отражающий поведение реального пользователя, который использует созданный нами продукт для достижения определенной цели.

Если смотреть на e2e тесты с точки зрения терминологии тестирования, можно сказать, что они ближе к вариантам использования, а не тест-кейсам – см. определения ниже:

Тест-кейс: конкретная ситуация в тестировании, в которой тестировщик выполняет действие или набор действий, при конкретных предусловиях, состоянии окружения и данных, чтобы валидировать свои убеждения и/или ожидания от функциональной корректности продукта.

Вариант использования: сценарий, в котором конечный пользователь (отсюда название – «вариант использования») выполняет цепочку действий в продукте или идет по определенному пути, следуя к конкретной конечной цели. Зачастую задействовано несколько функциональностей, подсистем и внешних результатов.

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

Учитывая вышесказанное, e2e-тесты намеренно кросс-функциональны – они прыгают от одной функциональности к другой, иногда – даже с помощью внешних приложений или ресурсов (почтовый агент для проверки получения верификационного письма, и т. д. ). Если ваш тест задействует только одну функциональность – то он скорее функциональный, а не end-to-end.

Он должен охватывать сценарии целиком, учитывая два измерения

Обсуждая end-to-end тесты, можно легко задаться вопросом, о каких концах речь? Где они? Мой ответ: в e2e-тестах мы хотим растянуть тест-покрытие в двух измерениях.

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

Такой сценарий может выглядеть как-то так:

Клиент авторизуется в приложении электронной коммерции, ищет конкретный продукт – скажем, худи, — открывает и изучает его карточку, сравнивает с другим товаром, оформляет заказ, выбирает метод оплаты, платит на странице внешнего оператора платежей и завершает заказ.

Какой набор проверок нам тут желателен?

  • В корзину добавлен правильный товар (с верной ценой и скидкой, если она есть)
  • Показан правильный экран успешной операции
  • Во внутренней системе добавлен заказ
  • Пользователю отправлено правильное письмо с информацией о заказе
  • Проверка правильной обработки платежа, и т. п.

Вертикальное e2e – включает все технические слои: бэкэнд, фронтэнд, сервисный слой, слой хранения и управления данными. Без исключений и имитаторов, кроме внешних зависимостей, если они не так важны. К примеру, можно использовать имитатор внешнего оператора платежей или их тестовое окружение.

Он должен соответствовать вариантам использования или приемочным критериям

Приемочные критерии и варианты использования – фактор, который часто упускают и в тестировании, и в разработке. Это легко объясняется тесными временными рамками релизного процесса. Работа с ними и их внедрение в e2e-тесты – хорошее упражнение для критического мышления. В целом тут речь о том, что при релизе ПО мы должны ответить на вопрос, в чем цель того, что мы делаем. Это ПО, приложение, игра, бинарник, нативное облачное приложение – в чем его цель?

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

Приемочные критерии – это разновидность контракта, который мы подписываем, чтобы это валидировать.

Откуда идет термин «приемочное тестирование/приемочный тест»?

Давным-давно, когда создание ПО было привилегией лишь нескольких специализированных компаний, обычные предприятия подписывали контракт на проекты по разработке с вышеупомянутыми специалистами. В этом контракте задавались приемочные критерии – набор критериев, которыми должно обладать ПО, или тестов, которые оно должно успешно пройти или продемонстрировать принимающей стороне, дабы она приняла работу и выплатила остаток. Надеюсь, сейчас вы понимаете, как глупо звучат заявления продуктовых компаний вроде «мы проводим пользовательское приемочное тестирование» – они же выступают и в качестве поставщика, и в качестве приемщика (еще и пользователя, если они решают «самостоятельно попробовать свой собачий корм»).

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

Он должен выполняться в конце релизного цикла, после прочих фаз тестирования

Уже чувствую волну возмущения этим заголовком, поэтому, пожалуйста, обратите внимание – я сказал «после», а не «вместо» прочих фаз тестирования – пожалуйста, сохраняйте спокойствие до конца статьи.

E2E-тестирование должно быть церемонией коронации процесса поставки. Ожидается, что все функциональные и парафункциональные баги уже выловлены и исправлены ранее. В момент автоматизированного прогона e2e его задача – верифицировать соответствие ожиданиям конечного пользователя.

Он должен быть частью критериев готовности (DoD) – устного контракта, которому мы должны следовать ради релиза

Многие компании, с которыми я работал, тряслись от ужаса при вопросе о DoD – позор им, позор! Часть работы команды, разрабатывающей ПО, включая тестировщиков и разработчиков – это определить набор критериев, позволяющих оценить готовность всех процессов, которые мы считаем необходимыми. Уверен, вас не удивит, если я скажу, что многие убеждены в готовности продукта еще до того, как его впервые увидит тестировщик или тест-инструмент. Эти компании страдают, порядочно страдают.

E2e – жизненно важная часть DoD, и если вы цените качество своей работы, ваша цель сделать его как можно более прозрачным: все задействованные стороны, даже вне технической части, ясно понимают, почему работа считается завершенной. Заметать что-то под ковер «слишком технического, вам не понять» – это демонстрация слабости.

Чем e2e тесты не являются, чего они не должны делать?

Они не должны исчерпывающе тестировать всю функциональность

Ставлю свою карьеру на то, что основная причина нестабильных тестов, помимо нажимающих на клавиши органических полуавтоматов (например, создателей), в том, что люди стараются засунуть в них все на свете. Я видел тесты, проверяющие разметку, путь, видимость кнопки, видимость результата, и т. д., и т. п. Получается свалка, а не e2e тест – тест переполнен чересчур большим количеством действий и в итоге теряет концентрацию и направление. Большинство этих тестов имеет смысл, задачу и причину, но не относится к e2e:

  • Тесты разметки и отображения/скрытия элементов можно легко провести в рамках компонентных тестов фреймворка фронтэнда.
  • Тестирование потоков и переходов между страницами – часть функционального тестирования.

E2e должны проверять только прямое, похоже на пользовательское, взаимодействие с продуктом.

Они – не оправдание для игнорирования всех прочих видов тестирования (юнит, низкоуровневая интеграция, нефункциональные, функциональные, исследовательские тесты)

То, что люди склонны складывать в e2e все на свете, приводит к еще одному побочному эффекту – они убеждены, что им больше не нужны штуки вроде юнит-тестов, интеграционного или функционального тестирования – у них же все это (или нечто похожее) уже есть, в e2e тестах. Это не так по ряду причин:

  1. Если смешивать разные типы тестирования, каждый из них утратит фокус.
  2. Если смешивать юнит/интеграционные и функциональные тесты, теряется возможность поэтапного тестирования, создающего ворота качества – это значит, что если отказывают ваши юнит/интеграционные/функциональные тесты, до e2e еще даже дело не дошло.
  3. Возрастают затраты на тестирование – легче, быстрее и приятнее прогонять низкоуровневые тесты, отлавливая баги.

Их недостаточно

Как уже говорилось выше, e2e тесты – это «счастливый путь», они не нацелены на абсолютное покрытие, они не пытаются глубоко тестировать функциональность. Если вы полагаетесь исключительно на них, вы по сути ведете себя так, как будто все прочее возможное тестирование не существует.

Они не предназначены для тестирования парафункциональных аспектов – производительности, безопасности, совместимости (с браузерами, носителями, окружениями), устанавливаемости, и т. д.

Это практическое, а не принципиальное замечание – e2e тесты предназначены для тестирования с точки зрения пользователя и только пользователя.

Любое машинное тестирование, нацеленное на нефункциональные характеристики вроде удобства использования, производительности, безопасности и т. п., имеет свои собственные тест-прогоны, сценарии и зачастую даже инфраструктуру. Это не универсальные тесты и не универсальные инженеры – я видел компании, где один и тот же тестировщик пытался заниматься функциональными тестами, e2e тестами, тестами производительности и безопасности: результаты были (если вообще были) жалким зрелищем. Если вы серьезно относитесь к какой-то из этих характеристик, наймите специализированного эксперта, чтобы этим тестированием занимался он, а не перекладывайте все на плечи несчастного автоматизатора e2e.

Они не оправдание для проведение несложного подтверждающего тестирования просто потому, что «мы это автоматизировали»

Это какая-то болезнь в нашей отрасли – создание тестов, которые не тестируют, а просто подтверждают, демонстрируют, что продукт на месте и работает. E2e тесты созданы как сценарии счастливого пути, и из-за их природы легко удариться в создание несложных подтверждающих проверок. Грань тут тонка, будьте осторожны! Мой вам совет:

  • Старайтесь, чтобы ваши тесты были максимально детерминированы
  • Добавляйте релевантные и значимые для контекста проверки. Столько, сколько сможете добавить и поддерживать.
  • Не полагайтесь на «мягкие ассерты» вроде ожиданий – да, ожидание обвалит тест, но нам нужен детерминированный способ понимания, почему он упал. Падение теста из-за обнаружения бага – это хорошо; плохо, если тест упал, а почему – вы не знаете.

Вот мой краткий список того, как надо и как не надо обращаться с e2e-тестированием – я иронично назвал его «Манифестом E2E-тестирования», но этого недостаточно. Будем продолжать открытый диалог – что думаете вы, какие примеры и антипримеры знаете? Дайте мне знать.

Добавить комментарий

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