На тему построения системы автоматизации тестирования в рамках конвейера CI/CD написано не мало различных публикаций. В этой статье мы хотим рассказать о том, как строился процесс тестирования одного продукта в реальной организации, какие сложности и ошибки возникали и как они решались.
Исходные данные
Разрабатывалось решение (Web портал) интеграционного типа, то есть оно получало данные от пользователя и отправляло эти же значения в разных форматах другим приложениям. То есть, клиенту необходимо заказать какой‑либо товар на сайте. Он предоставлял данные о заказе нашему программному обеспечению для создания карточек заказа необходимых для доставки данного товара получателю. В своем приложении мы храним эту информацию в СУБД, но так как наша система тесно интегрируется со сторонними системами (посредством API), то карточки заказов реструктурируются в другие форматы, чтобы логистические агрегаторы (компании, которые сотрудничают с несколькими курьерскими и транспортными компаниями для предоставления логистических решений в области электронной коммерции) могли обработать заказ и обеспечить доставку посылки.
Здесь основная проблема заключается в том, что нам тестировать не только функционал непосредственно нашего приложения, но также и то, что связано с передачей карточек товаров в сторонние системы. Данные в эти системы передавались в формате PDF, так как нам необходимо было обеспечить неизменность данных о заказе. Дальше уже на стороне этих приложений осуществлялось распознавание содержимого карточки. Таким образом, в процессе тестирования нам нужно было убедиться, что данные содержащиеся в этих PDF соответствуют тому, что изначально указал клиент.
Первая версия
Не имея достаточных знаний и опираясь на уже стабильный набор ручных тестов, был создан минимальный набор тестов, необходимых для проверки работоспособности функционала. При этом, не было даже какого‑то подобия плана автоматизации тестирования.
Для исправления этой излишне ручной ситуации мы решили воспользоваться фреймворком для написания тест‑кейсов Junit4 и фреймворком для вызова API RestAssure.
Принцип работы системы тестирования был следующим: приложение выполняло POST‑вызовы к API, а ответные данные проверялись. Также были добавлены утверждения Assert (проверка совпадения ожидаемого и фактического значения). В целом получившаяся система хорошо проверяла базовый функционал приложения, но были некоторые недостатки.
Так, мы не выполняли проверки содержимого таблиц СУБД. То есть мы не знали, совпадают ли значения, хранящиеся в базе данных, с тем, что было отправлено в карточках заказов, в сторонние системы. Получалось, что мы проверяли то, что отправлялось другим, но при этом не смотрели что собственно сохранялось у нас, полагая, что если уж на сторону ушли корректные данные, то в нашей системе они тем более будут правильные.
Естественно, такой подход был не совсем правильным и мы продолжили развитие нашей системы тестирования.
Подключаем БД
До этого все проверки содержимого БД осуществлялись в ручном режиме. То есть существовал некоторый набор тестов и для проверок мы использовали дамп данных, который восстанавливался каждый раз перед тестами.
Нами двигала идея создать лучшую архитектуру. То есть мы хотели добавлять только нужные значения в БД, чтобы сэкономить время на загрузке дампа. Проще говоря, вместо дампа у нас был создан базовый набор команд SQL, который генерировал содержимое таблиц.
Для тестирования мы использовали пакет java.sql, который предоставлял API для выполнения запросов к базе данных и получения результатов запроса для утверждения.

Но при этом у нас все‑равно использовался дамп, который создавался вручную для постоянных значений, таких как процент НДС по умолчанию, страна, почтовые индексы и т. д. И только после создания ручного дампа запускались SQL скрипты для наполнения содержимого таблиц.
Автоматизированные тесты тоже были неполными. Так были функции, которые проверяли только наличие данных в соответствующих таблицах, но не их значение.
Например: assertNotNull(result.next())
.
Существенным недостатком являлось то, что хотя набор позволял протестировать программное обеспечение, но когда один тест проваливался, практически все последующие проваливались, потому что при провале теста не выполнялись запросы на удаление, что приводило к провалам в других случаях.
Также, данные запроса и ответа должны совпадать с данными в БД. Но в данной версии тестового набора это отсутствовало. Были сделаны вызовы сторонних приложений через API. Но эти вызовы необходимо имитировать и найти способы перепроверки значений, отправляемых этим приложениям.
Проверяем PDF
На следующем шаге мы уже смогли начать говорить о надежности. Используя библиотеку PDFBox, (открытый инструмент на языке Java для работы с документами в формате PDF) мы смогли проверить, что все, что было указано в базе данных, правильно распечатывается в PDF‑фактуре. Приложение Mockoon использовалось для имитации API‑вызовов к логистическим агрегаторам. Благодаря этому приложению нам не требовался сторонний бекэнд, так как все запросы мы могли имитировать самостоятельно. Запрос и ответ записывались в ELK (Elastisearch — место, где хранится информация обо всех API и их ответах для целей отладки).
PDFBox используется для получения значений из определенного источника. Использование библиотеки помогло в перекрестной проверке значений, напечатанных в счете, со значениями, отправленными для генерации данного счета.
Набор автоматизированных тестов казался функциональным, но далеко не полным из‑за отсутствия проверок базы данных. Как мы помним, когда один тест проваливался, это часто приводило к тому, что последующие тесты также проваливались из‑за невыполненных запросов на удаление.
Это можно было бы легко решить с помощью аннотаций @AfterMethod(testng)
или @Before(junit4)
, что мы и сделали в следующей версии.
Боремся с дампами
В этой версии мы создали отдельные файлы дампа (просто копия всей базы данных в одном файле) для каждого основного сценария и восстанавливали дамп каждый раз, когда выполнялся тестовый пример.
Каждый случай будет выполняться на той же базе данных с теми же значениями, что и предыдущий, что позволило решить основную проблему, связанную с отказом зависимых тестовых случаев, вызванным невыполненными запросами на удаление.
Однако в тестовом наборе возникла проблема производительности размером со слона. На выполнение каждого кейса уходило около минуты, а так как к этому времени в наборе было уже более 400 кейсов, что в итоге привело к шести с лишним часам на выполнение всех проверок.
Кусочек кода в помощь
Для решения этой проблемы мы добавили в код нашего тестового набора фрагмент под названием «db_connect», который представлял собой микросервис, запущенный на машине, где работало приложение.
Этот микросервис получал некоторые инструкции от тестового пакета и, основываясь на них, восстанавливал файлы дампа, размещенные в бакете облачного сервиса, в базе данных после каждого теста.
Восстановление дампов происходило очень быстро, поскольку сетевые экземпляры нашей БД тоже находились в этом облаке и в одной сети, что сократило время восстановления с 1 минуты до всего 4 секунд! В результате проверки стали намного производительнее.

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

Наши тесты запускались сразу после сборки кода. При этом, сначала выполнялись проверки содержимого Базы Данных, а затем уже проверялись непосредственно API функции, когда мы имитировали сторонние приложения и PDF карточки заказов, которые отправлялись сторонним приложениям.
Заключение
Помимо тех шагов, которые были представлены выше в статье, нам также необходимо было выполнить «смещение влево», то есть в сторону разработки для того, чтобы получить покрытие кода для тестового набора. Взаимодействуя с разработчиками и извлекая из исходного кода информацию о выполняемом приложением функционале мы смогли выполнить стопроцентное покрытие кода нашими тестами, что позволило обеспечить выпуск надежных релизов нашего приложения.
Если тема автоматизации тестирования вам близка — обратите внимание на три открытых урока, которые пройдут в рамках курсов OTUS. Они помогут систематизировать знания, навести порядок в тестовой документации, углубиться в CI/CD и освоить Postman для API-проверок.
5 июня в 20:00
Тестовая документация на практике: от хаоса к понятной системе чек-листов и багов10 июня в 20:00
Jenkins Job Builder: автоматизируем развёртывание jobs и упрощаем CI/CD процесс19 июня в 20:00
Postman + JavaScript: быстрый старт в автоматизированном тестировании
Больше актуальных навыков по тестированию вы можете получить в рамках практических онлайн-курсов от экспертов отрасли.
dopusteam
Читателю предлагается самому догадаться, что это за цифры, и почему одна выделена красным?