Тестирование на высоком уровне улучшит ваше понимание функции и позволит вам сосредоточиться на конечной цели.

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

Что такое интеграционный тест?

Интеграционное тестирование (иногда называемое интеграцией и тестированием, сокращенно I&T) - это этап тестирования программного обеспечения, на котором отдельные программные модули объединены и протестированы как группа. Интеграционное тестирование проводится для оценки соответствия системы или компонента заданным функциональным требованиям.

-Википедия, Интеграционное тестирование

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

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

Хорошо, конечно, но разве это не одно и то же?

К сожалению нет. Давайте рассмотрим пример.

Допустим, ваше приложение отвечает за отслеживание баланса счета клиента. Когда происходит транзакция, баланс учетной записи должен измениться на сумму транзакции и быть доступным для запроса через RESTful API. Это наша особенность.

Вы можете создать несколько компонентов:

  • Контроллер REST для предоставления конечной точки для обработки события транзакции
  • Объект доступа к данным для хранения и извлечения транзакции.
  • Сервис для расчета баланса счета на основе транзакции (транзакций)
  • Контроллер REST для отображения баланса аккаунта

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

Введите: интеграционный тест

Интеграционный тест проверит ваше приложение на уровне функций. Он проверяет, что интеграция ваших компонентов имеет ожидаемую функциональность. Я слышал, что интеграционные тесты называются функциональными тестами или регрессионными тестами. Давайте посмотрим на пример в псевдокоде:

fun test() { 
  app.start() 
  assert GET /TEST_USER/balance is 0 
  POST /TEST_USER/transaction {... "quantity": 42 ...} 
  assert GET /TEST_USER/balance is 42 
}

Этот тест подтверждает, что наша функция выполняет то, что мы обещали. Этот вид проверки поддается краткости.

Интеграционные тесты гарантируют, что вы проверите, что имеет значение: функция

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

Сосредоточение внимания на функции упростит процесс разработки, позволяя сосредоточиться на конечной цели. В конце концов, вторая привычка Стивена Кови - «Начни с конца». Это также оттолкнет разработчиков от написания кода, который «может понадобиться позже». Отказ от ответственности: вам все равно следует писать чистые модульные компоненты.

Интеграционное тестирование на практике

Есть много способов выполнить интеграционное тестирование. Наиболее очевидным является простая проверка функциональности вручную в среде более низкого уровня (непроизводственной). Не надейтесь на это!

Мало того, что на проверку всех тестовых примеров для функции будет потрачено меньше усилий, так и тест почти наверняка не будет повторяться в будущих выпусках. В любом случае, какими бы мы были программистами, если бы это не было автоматизировано? Интеграционные тесты должны быть:

  1. Автоматизировано в конвейере сборки CI / CD
  2. Легко запускать и редактировать во время разработки
  3. Поддерживается рамкой специализированного кода, который упрощает написание и чтение тестов.

Пример инфраструктуры тестирования интеграции на Java

Во всех наших микросервисах мы использовали абстрактный родительский класс IntegrationTest.java, который использует Spring Boot @SpringBootTest для запуска приложения и обрабатывает имитацию всех внешних зависимостей. Для внешних HTTP-ресурсов мы используем Wiremock, базы данных используют БД в памяти типа H2. Мы дошли до того, что написали собственную реализацию слоя сообщений AMPS для тестирования.

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

Все, что нужно сделать разработчику, - это создать новый класс, расширить родительский IntegrationTest.java и начать писать тесты JUnit. Spring Boot будет повторно использовать контекст приложения, где это возможно, чтобы сократить время, затрачиваемое на выполнение тестов.

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

Интеграционные тесты кодируют функции в вашем приложении

Как бы я ни любил писать документацию, это не всегда удается. Документация также может быть устаревшей (ложной). Хорошо написанные интеграционные тесты являются живым документом ваших функций и делают ваше приложение:

  1. Более четкое представление о том, какой код поддерживает какие функции (что упрощает понимание новыми разработчиками).
  2. Менять безопаснее, потому что затронутые функции потребуют внесения изменений в тесты.

Это похоже на то, что такое Behavior-Driven Development (BDD), но без всяких огурцов. Помимо шуток, участие в написании тестов заинтересованных лиц нетехнического профиля - это фантастика, но это также кажется несбыточной мечтой. В конечном итоге ответственность за понимание потребностей пользователя ложится на инженера.

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

Ключевые выводы

  • Интеграционные тесты проверяют, что ваше приложение работает правильно.
  • Вы должны проверить, что имеет значение: функция.
  • Функции должны быть закодированы в вашей среде тестирования интеграции.
  • Интеграционные тесты должны быть простыми в написании, обновлении и иметь какой-либо метод автоматизации.
  • Интеграционные тесты должны сделать ваше приложение более понятным и безопасным.