Сам по себе термин “интеграционные тесты” один из самых вводящих в заблуждение: возьми несколько программистов, спроси что такое интеграционные тесты, и каждый расскажет что-то свое.
Примеры определений:
- тест покрывает какие-то сценарий (от http-запроса до запросов к БД), запросы к тестовой БД, поднятой в testcontainers, внешние сервисы замокированы
- тест покрывает какие-то сценарий (от http-запроса до запросов к БД), запросы к БД и внешним сервисам замокированы
- тестируется один сервис в полноценном тестовом окружении
- тестируются несколько сервисов одновременно в каком-то тестовом окружении
- тестируется один метод, который работает с какой-то внешней системой (БД, внешний сервис), внешняя система поднята через testcontainers
Хочу остановиться на 1ом-2ом варианте: это наиболее простой способ поднять показатель покрытия тестами с нуля процентов до 60-70. А вот потом уже каждый процент будет даваться все сложнее и сложнее.
Почему просто? Каждый тест сразу проходит много кода. Покрыв основные положительные и отрицательные сценарии (обычно их мало, иначе они не основные), сразу видим хорошую цифру.
А в чем минусы?
- Медленно запускается. Все-таки не юнит-тест.
- Так же легко появляются комбинаторные сценарии: на первом уровне 3 выбора, на втором еще 3, так что нужно писать 9 тестов. В отличие от 6, когда тесты пишутся изолированно.
- Покрывать неосновные сценарии сложнее, чем основные, т.к. для этого еще ничего не готово: чтобы избежать комбинаторных эффектов переходят от интеграционных тестов к юнит, а там еще ничего не готово: ни сам файл не создан, ни данные.
Какой выход? Тестировать каждую публичную функцию класса отдельно с мокированием всех зависимостей класса:
- в этом случае не проявляется комбинаторный эффект, так что совершенно очевидно как повышать покрытие и оно линейно по времени
- технически это обычно чистый unit-тесты, а, значит, быстро работают
- когда нужно протестировать взаимодействие с внешней системой, то можно использовать testcontainers, а в остальном такой же принцип. В этом случае взаимодействие протестировано, но таких тестов минимальное кол-во, что положительно сказывается на скорости работы тестов
- подход не накладывает ограничения как именно разрабатывать систему
Некоторые называют это лондонской школой юнит-тестирования.
В итоге, мне ближе последнее определение для интеграционных тестов.
При этом понятно, что всю систему в целом так же хочется проверить. Для этого лучше использовать так называемые тесты на общую работоспособность (smoke-тесты). Но об этом поговорим в отдельной статье.