Короткий ответ
Unit-тест проверяет изолированный фрагмент логики (функцию, метод, класс) без реальных внешних зависимостей, а интеграционный тест проверяет совместную работу нескольких реальных компонентов — например, кода и тестовой БД.
Как это работает подробнее
Unit-тест подменяет окружение (БД, сеть, файловую систему) моками или заглушками и проверяет поведение одной единицы кода на заданных входных данных, изолируя её от остальной системы. Интеграционный тест запускает несколько компонентов вместе — приложение и реальную тестовую БД, приложение и очередь сообщений, — проверяя их фактическое взаимодействие, включая сериализацию данных и реальные сетевые или протокольные детали.
Пример кода
testing_example_291.py
def calculate_discount(price: float, is_vip: bool) -> float:
return price * 0.8 if is_vip else price
def test_calculate_discount_for_vip_user():
# unit-тест: нет обращений к БД, сети или файловой системе
assert calculate_discount(100.0, True) == 80.0
# Интеграционный тест (другой уровень) обращался бы к тестовой БД,
# чтобы получить is_vip из реальной таблицы пользователей.Что использовать на практике
- Практика. Unit-тесты быстрые и дешёвые, их должно быть больше всего — это нижний уровень тестовой пирамиды, и их стоит запускать при каждом сохранении файла или коммите. Интеграционные тесты медленнее и дороже в поддержке, но ловят ошибки на границах компонентов: несовпадение типов колонок в SQL, неверный формат ответа внешнего API, которые мок не заметит, поэтому их обычно держат в отдельном, более редком этапе CI.
Подводные камни
- Риск. Называть unit-тестом тест, который фактически стучится в реальную БД или сеть — это интеграционный тест, просто маленький, и его стоит запускать и учитывать отдельно. Считать, что большое число unit-тестов с моками само по себе гарантирует работоспособность системы в целом, хотя взаимодействие реальных компонентов — форматы запросов, транзакции, сетевые тайм-ауты — нигде не проверено.