Тестирование на высоком уровне улучшит ваше понимание функции и позволит вам сосредоточиться на конечной цели.
В своей консультационной работе с инвестиционными банками я создавал набор микросервисов, работающих во внутреннем частном облаке. Это приложение имеет решающее значение для плана создания нового продукта, который недавно был запущен с нашим первым клиентом. Перед запуском мы выпускали производственные релизы не реже двух раз в неделю. Причина и проблемы с этим являются темой для отдельного сообщения - интеграционное тестирование того, как нам удалось поддерживать частые производственные выпуски в составе небольшой группы разработчиков из 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
}
Этот тест подтверждает, что наша функция выполняет то, что мы обещали. Этот вид проверки поддается краткости.
Интеграционные тесты гарантируют, что вы проверите, что имеет значение: функция
Легко потратить свое время на разработку, сосредоточившись на написании чистых модульных компонентов. Все мы чувствуем зуд создать что-то прекрасное, даже если этого никто никогда не увидит. Писать красивый код без цели - тщеславие. Процесс написания настоящего интеграционного теста потребует от вас взаимодействия с приложением в контексте функции, а не технологии.
Сосредоточение внимания на функции упростит процесс разработки, позволяя сосредоточиться на конечной цели. В конце концов, вторая привычка Стивена Кови - «Начни с конца». Это также оттолкнет разработчиков от написания кода, который «может понадобиться позже». Отказ от ответственности: вам все равно следует писать чистые модульные компоненты.
Интеграционное тестирование на практике
Есть много способов выполнить интеграционное тестирование. Наиболее очевидным является простая проверка функциональности вручную в среде более низкого уровня (непроизводственной). Не надейтесь на это!
Мало того, что на проверку всех тестовых примеров для функции будет потрачено меньше усилий, так и тест почти наверняка не будет повторяться в будущих выпусках. В любом случае, какими бы мы были программистами, если бы это не было автоматизировано? Интеграционные тесты должны быть:
- Автоматизировано в конвейере сборки CI / CD
- Легко запускать и редактировать во время разработки
- Поддерживается рамкой специализированного кода, который упрощает написание и чтение тестов.
Пример инфраструктуры тестирования интеграции на Java
Во всех наших микросервисах мы использовали абстрактный родительский класс IntegrationTest.java, который использует Spring Boot @SpringBootTest для запуска приложения и обрабатывает имитацию всех внешних зависимостей. Для внешних HTTP-ресурсов мы используем Wiremock, базы данных используют БД в памяти типа H2. Мы дошли до того, что написали собственную реализацию слоя сообщений AMPS для тестирования.
Мы также пишем несколько настраиваемых фабрик для генерации запросов и тестовых данных, простых в использовании API-интерфейсов для взаимодействия с макетами и канонических значений по умолчанию для справочных данных. Это значительно снижает порог входа для написания тестов.
Все, что нужно сделать разработчику, - это создать новый класс, расширить родительский IntegrationTest.java и начать писать тесты JUnit. Spring Boot будет повторно использовать контекст приложения, где это возможно, чтобы сократить время, затрачиваемое на выполнение тестов.
Каждый тестовый класс представляет собой одну функцию. Тесты в тестовом классе описывают реальные варианты использования и проверяют фактический результат работы приложения, работающего в тестовой среде.
Интеграционные тесты кодируют функции в вашем приложении
Как бы я ни любил писать документацию, это не всегда удается. Документация также может быть устаревшей (ложной). Хорошо написанные интеграционные тесты являются живым документом ваших функций и делают ваше приложение:
- Более четкое представление о том, какой код поддерживает какие функции (что упрощает понимание новыми разработчиками).
- Менять безопаснее, потому что затронутые функции потребуют внесения изменений в тесты.
Это похоже на то, что такое Behavior-Driven Development (BDD), но без всяких огурцов. Помимо шуток, участие в написании тестов заинтересованных лиц нетехнического профиля - это фантастика, но это также кажется несбыточной мечтой. В конечном итоге ответственность за понимание потребностей пользователя ложится на инженера.
Когда интеграционные тесты являются основным методом тестирования, используемым разработчиками, модульные тесты не нужны. Если все функции вашего приложения закодированы в тестах, весь ваш код должен быть покрыт. Еще лучше, все ваши компоненты будут протестированы в контексте этой функции. Любые компоненты или ветви, не охваченные интеграционным тестом, являются посторонними.
Ключевые выводы
- Интеграционные тесты проверяют, что ваше приложение работает правильно.
- Вы должны проверить, что имеет значение: функция.
- Функции должны быть закодированы в вашей среде тестирования интеграции.
- Интеграционные тесты должны быть простыми в написании, обновлении и иметь какой-либо метод автоматизации.
- Интеграционные тесты должны сделать ваше приложение более понятным и безопасным.