Дорогой читатель, это мой первый туториал и если я что‑то упустил или не объяснил, хотя стоило бы, напиши пожалуйста комментарий и я обновлю статью.
TL;DR
Весь код для микросервисов и e2e теста тут
Рекомендую быстро пробежаться по коду перед прочтением статьи т к в самой статье я объясню только неочевидные моменты реализации 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 используя русский язык
