Вопросы
- Какие виды тестов нужны?
- Где проходит граница между unit-, integration- и end-to-end-тестами?
- Как понять, что тестов достаточно?
- Что делать с flaky tests?
- Как тестировать микросервисы?
- Как вы строите стратегию тестирования?
Какие виды тестов нужны?
Для лида хороший ответ должен быть не «нужны unit-, integration- и
end-to-end-тесты», а объяснение того, какие риски каждый вид тестов
закрывает.
Развёрнутый ответ
Я не выбираю тесты по формальному списку. Сначала я смотрю, какие риски есть
у изменения: бизнес-логика, интеграции, база данных, API-контракты, UI,
производительность, безопасность, миграции, совместимость. После этого
подбираю нужный набор тестов.На базовом уровне нужны unit tests — они проверяют отдельную
бизнес-логику, алгоритмы, валидации, маппинг и граничные случаи. Они быстрые
и помогают ловить ошибки ещё до интеграции с другими компонентами.Дальше нужны integration tests — они проверяют взаимодействие
компонентов: сервиса с базой данных, очередью, внешним API, Redis, Kafka,
PostgreSQL и другими зависимостями. Это важно, потому что часто код отдельно
работает, а ломается именно на стыке компонентов.Для микросервисов и внешних клиентов важны contract tests. Они
проверяют, что изменения API, событий, payload, схем сообщений и форматов
данных не сломали потребителей. Это особенно важно для предотвращения
breaking changes.Перед релизом нужны regression tests, чтобы убедиться, что изменение не
сломало существующую функциональность. Это может быть автоматизированный
набор тестов или комбинация автотестов и ручной проверки критичных
сценариев.Для пользовательских сценариев нужны end-to-end tests — они проверяют
полный путь пользователя через систему. Например: логин → выбор продукта →
оплата → подтверждение → создание записи. Таких тестов не должно быть
слишком много, потому что они дорогие и нестабильные, но критичные
бизнес-пути должны быть покрыты.Также нужны smoke tests после деплоя. Это быстрые проверки того, что
система поднялась, основные endpoints отвечают, UI открывается, логин
работает и ключевые операции доступны.Если изменение связано с нагрузкой, нужны performance/load tests:
проверка latency, throughput, потребления памяти и поведения под пиковым
трафиком.Если затронута безопасность, нужны security tests: проверка авторизации,
доступов, ролей, инъекций и утечек данных.Если есть миграции базы данных, отдельно нужны migration tests: корректно
ли применяется миграция, сохраняются ли данные, возможен ли откат, совместимы
ли старая и новая версии приложения с переходным состоянием базы.
Короткий ответ для собеседования
Нужны разные уровни тестов под разные риски: unit tests для бизнес-логики,
integration tests для взаимодействия компонентов, contract tests для API и
сообщений между сервисами, regression tests для защиты существующего
поведения, end-to-end tests для критичных пользовательских сценариев, smoke
tests после деплоя, performance tests для нагрузки, security tests для
доступа и уязвимостей, migration tests для изменений базы данных. Как лид, я
смотрю не на количество тестов, а на то, закрывают ли они реальные риски
релиза.
Лидовская формулировка
Для меня правильная test strategy — это не максимальное покрытие всего
подряд, а разумное покрытие критичных рисков. Быстрые тесты должны ловить
ошибки рано, интеграционные — проблемы на стыках, контрактные — защищать
потребителей, end-to-end — подтверждать ключевые бизнес-сценарии, а smoke
tests и monitoring после релиза — показывать, что production действительно
работает.
Виды тестов и риски
| Вид тестов | Что проверяют |
|---|---|
| Unit tests | Отдельную бизнес-логику |
| Integration tests | Взаимодействие компонентов |
| Contract tests | Совместимость API, событий и сообщений |
| Regression tests | Что существующее поведение не сломалось |
| End-to-end tests | Полный пользовательский сценарий |
| Smoke tests | Что система работает после деплоя |
| Migration tests | Безопасность изменений базы данных |
| Performance/load tests | Нагрузку, latency и throughput |
| Security tests | Доступы, роли и уязвимости |
| Manual exploratory tests | Нестандартные сценарии, не покрытые автотестами |
Главная мысль: лид не просто перечисляет виды тестов, а связывает каждый тест
с конкретным риском production-системы.
Где проходит граница между unit-, integration- и end-to-end-тестами?
Граница проходит не по названию технологии, а по размеру проверяемого
поведения и количеству реальных зависимостей.
Развёрнутый ответ
Я разделяю unit-, integration- и end-to-end-тесты не формально, а по уровню
поведения, которое они проверяют.Unit-тест проверяет маленький изолированный кусок логики: функцию,
класс, метод, доменный сервис, валидатор, маппер, расчёт, бизнес-правило.
Его цель — быстро подтвердить, что конкретная логика работает правильно. В
unit-тесте внешние зависимости обычно заменяются mock, stub или fake-объектами:
база данных, сеть, очередь сообщений, внешнее API, файловая система. Такой
тест должен быть быстрым, стабильным и точно показывать, где сломалась
логика.Integration-тест проверяет, что несколько компонентов системы правильно
работают вместе. Например: сервис + репозиторий + реальная база данных;
REST-контроллер + security + service layer; producer + Kafka; приложение +
Redis; модуль авторизации + Keycloak test container. Здесь нас интересует не
только бизнес-логика, а контракт между частями системы: корректные SQL-запросы,
транзакции, сериализация, конфигурация, wiring, миграции, работа с
инфраструктурными компонентами. Integration-тест обычно медленнее unit-теста,
но он ловит ошибки, которые unit-тесты не увидят.End-to-end-тест проверяет пользовательский или бизнес-сценарий целиком,
максимально близко к реальному использованию системы. Например: пользователь
логинится, создаёт заказ, оплачивает его, получает подтверждение, а заказ
появляется в админке. Здесь проверяется весь путь через UI или API, backend,
базу, очереди, внешние интеграции или их тестовые аналоги. E2E-тесты самые
дорогие, медленные и хрупкие, поэтому их не должно быть слишком много. Они
нужны для критических happy path и ключевых бизнес-процессов.Если коротко:
unit отвечает на вопрос: правильно ли работает конкретная логика?
integration отвечает: правильно ли компоненты соединены и взаимодействуют?
end-to-end отвечает: работает ли весь бизнес-сценарий глазами пользователя
или системы?Как лид я бы не пытался покрывать всё одним типом тестов. Я бы строил
пирамиду: много быстрых unit-тестов на бизнес-логику, меньше
integration-тестов на реальные границы системы и немного e2e-тестов на самые
важные пользовательские сценарии. Главная задача — получить быстрый feedback,
защититься от регрессий и не превратить тесты в тяжёлую нестабильную систему,
которую команда потом боится запускать.
Короткий ответ для собеседования
Граница между тестами проходит по степени изоляции и масштабу проверяемого
поведения: unit проверяет логику в изоляции, integration проверяет
взаимодействие компонентов, а e2e проверяет полный бизнес-сценарий через
реальные границы системы.
Как понять, что тестов достаточно?
Тестов достаточно не тогда, когда есть красивый процент покрытия, а когда
основные риски системы закрыты проверками, и команда может менять код без
страха сломать критическое поведение.
Развёрнутый ответ
Я не определяю достаточность тестов только по code coverage. Покрытие полезно
как индикатор, но оно не отвечает на главный вопрос: защищают ли тесты
систему от реальных регрессий.Для меня тестов достаточно, когда закрыты несколько уровней риска.
Во-первых, протестирована ключевая бизнес-логика: расчёты, правила,
валидации, переходы состояний, права доступа, обработка ошибок. То есть всё,
где ошибка может привести к деньгам, данным, безопасности или поломке
основного пользовательского сценария.Во-вторых, покрыты основные интеграционные границы: база данных, транзакции,
миграции, очереди, внешние API, сериализация, авторизация, конфигурация.
Очень часто баги возникают не внутри одного метода, а на стыке компонентов,
поэтому одних unit-тестов недостаточно.В-третьих, есть небольшой набор end-to-end-тестов на критические сценарии.
Не нужно проверять через e2e каждую мелочь, потому что такие тесты дорогие и
хрупкие. Но основные пользовательские пути должны быть защищены: логин,
создание ключевой сущности, оплата, оформление заказа, отправка сообщения,
обработка заявки — в зависимости от домена продукта.В-четвёртых, тесты должны реально помогать команде. Если при изменении кода
тесты быстро показывают, что сломалось, если они стабильны, запускаются в
CI/CD и не требуют постоянного ручного ремонта, значит тестовая база здорова.
Если же тестов много, но они flaky, непонятные и команда их игнорирует,
формально тесты есть, но защиты нет.Я бы смотрел на достаточность через риск, а не через количество. Для нового
функционала я задаю вопросы: что здесь самое критичное? где возможна
регрессия? какие сценарии пользователь реально проходит? какие внешние
зависимости участвуют? что будет, если эта часть сломается в production?Хороший критерий такой: тестов достаточно, когда команда может вносить
изменения в важные части системы, получать быстрый feedback и иметь разумную
уверенность, что критическое поведение не сломано. Но при этом тестов не
должно быть столько, чтобы они тормозили разработку и превращались в
отдельную хрупкую систему.
Короткая формулировка
Достаточность тестов я определяю не по проценту покрытия, а по закрытию
рисков: критическая бизнес-логика, интеграционные границы и основные
пользовательские сценарии должны быть защищены стабильными тестами, которые
дают быстрый feedback в CI.
Что делать с flaky tests?
Flaky tests — это не мелкая неприятность, а подрыв доверия к тестовой
системе. Если команда начинает думать “ну это тест просто моргнул”, CI
превращается из защитного механизма в шум.
Развёрнутый ответ
С flaky tests я бы работал как с отдельным инженерным риском, а не как с
чем-то, что можно просто игнорировать.Первое — flaky-тест нужно зафиксировать и воспроизвести. Я бы посмотрел,
падает ли он локально, падает ли только в CI, зависит ли от порядка запуска,
времени, нагрузки, окружения, параллельности или внешних сервисов. Часто
flaky tests появляются из-за race condition, неправильной работы с временем,
асинхронщины, shared state между тестами, нестабильных моков, реальной сети,
базы данных или недостаточно изолированного окружения.Второе — такой тест нельзя просто бесконечно перезапускать и делать вид, что
всё нормально. Retry может быть временной мерой, чтобы не блокировать команду
полностью, но он не должен скрывать проблему. Если тест нестабилен, нужно
завести задачу, назначить владельца и разобраться с причиной.Третье — если flaky-тест блокирует релизы и при этом не даёт надёжного
сигнала, его можно временно изолировать: вынести из обязательного pipeline,
пометить как quarantined, запускать отдельно и чинить в приоритетном порядке.
Но удалять его без анализа опасно, потому что иногда flaky-тест показывает
настоящий production-риск: гонку, проблему синхронизации или неустойчивую
интеграцию.Четвёртое — я бы устранял типовые причины. Для тестов со временем использовать
controllable clock вместо реального системного времени. Для асинхронных
операций — нормальное ожидание состояния вместо sleep. Для базы — изоляцию
данных, транзакции, test containers или отдельные схемы. Для внешних API —
contract tests, mock server или sandbox-окружение. Для параллельного запуска —
убрать shared mutable state и не полагаться на порядок выполнения тестов.Пятое — важно отслеживать flaky tests как метрику качества CI. Если
flaky-тесты регулярно появляются, это сигнал, что проблема не только в
отдельных тестах, а в архитектуре тестирования: плохая изоляция, нестабильное
окружение, слишком тяжёлые e2e-тесты или неуправляемые зависимости.Как лид я бы держал правило: тест либо даёт надёжный сигнал, либо мы его
чиним. Тестовая система должна быть источником доверия. Если команда
перестаёт верить тестам, то даже большое покрытие перестаёт иметь смысл.
Короткая формулировка
Flaky tests нельзя нормализовать. Их нужно расследовать как дефект
тестовой системы или как возможный симптом реальной проблемы в продукте:
изолировать, найти причину, починить и вернуть доверие к CI.
Как тестировать микросервисы?
Микросервисы нельзя тестировать как один большой монолит, где каждый тест
поднимает всю систему. Главная идея: каждый сервис тестируется как
самостоятельный продукт, а связи между сервисами проверяются через контракты и
ограниченный набор сквозных сценариев.
Развёрнутый ответ
Микросервисы я бы тестировал на нескольких уровнях.
Первый уровень — это unit-тесты внутри каждого сервиса. Здесь мы
проверяем бизнес-логику, валидации, расчёты, правила перехода состояний,
обработку ошибок. Эти тесты быстрые, изолированные и не зависят от сети,
базы данных или других сервисов.Второй уровень — component-тесты самого сервиса. На этом уровне сервис
поднимается почти целиком, но его внешние зависимости заменяются mock server,
stub, fake-реализациями или test containers. Например, можно поднять сервис с
реальной базой данных в контейнере, проверить REST API, persistence layer,
транзакции, миграции, сериализацию, security-конфигурацию. Это позволяет
убедиться, что сервис как самостоятельный компонент работает корректно.Третий уровень — contract tests между сервисами. В микросервисной
архитектуре это особенно важно, потому что сервисы общаются через API,
очереди, события или сообщения. Нужно проверять, что producer и consumer
одинаково понимают структуру данных, обязательные поля, форматы, статус-коды,
ошибки, версионирование и backward compatibility. Для HTTP API это могут быть
consumer-driven contract tests, например через Pact. Для событийной
архитектуры — проверка схем сообщений, event contracts, совместимости schema
registry, правил изменения событий.Четвёртый уровень — integration tests для реальных инфраструктурных
зависимостей. Например: сервис + PostgreSQL, сервис + Kafka, сервис + Redis,
сервис + S3-compatible storage, сервис + Keycloak. Здесь важно проверять не
абстрактную бизнес-логику, а реальное взаимодействие с инфраструктурой:
транзакции, ретраи, idempotency, обработку дублей, таймауты, eventual
consistency, dead-letter queue, rollback, миграции.Пятый уровень — end-to-end-тесты на ключевые бизнес-сценарии. Но их не
должно быть слишком много. В микросервисах e2e-тесты дорогие, медленные и
часто flaky, потому что зависят от многих сервисов, сети, данных и окружения.
Поэтому я бы использовал e2e только для критических happy path и нескольких
важных failure path: например, пользователь создаёт заказ, проходит оплата,
заказ попадает в обработку, уведомление отправляется, статус корректно
меняется.Отдельно я бы проверял устойчивость системы: таймауты, retry policy, circuit
breaker, idempotency, graceful degradation, обработку недоступности соседнего
сервиса, повторную доставку сообщений, несовпадение версий API. В
микросервисах очень много ошибок возникает не в коде одного сервиса, а на
границах: сеть, контракты, события, порядок сообщений, дубли, задержки,
частичные отказы.Как лид я бы не строил стратегию “поднять все микросервисы и прогнать всё
через e2e”. Это быстро превращается в тяжёлый и нестабильный pipeline. Я бы
строил тестирование так: каждый сервис максимально проверяется самостоятельно,
контракты между сервисами фиксируются автоматически, инфраструктурные
зависимости проверяются через integration tests, а сквозные e2e-тесты
покрывают только самые важные бизнес-процессы.Главная цель — получить быстрый feedback и уверенность, что изменения в
одном сервисе не ломают других потребителей.
Короткая формулировка
В микросервисах я тестирую не только код внутри сервиса, а прежде всего
границы: API-контракты, события, схемы сообщений, инфраструктурные
зависимости и поведение при частичных отказах. Каждый сервис должен быть
хорошо протестирован изолированно, а вся система — проверяться ограниченным
набором критических end-to-end-сценариев.
Сильная фраза
Микросервисная архитектура ломается не там, где метод неправильно посчитал
число, а там, где сервисы неправильно поняли друг друга.
Как вы строите стратегию тестирования?
Стратегия тестирования для лида — это не “давайте напишем побольше тестов”.
Это система защиты продукта от регрессий, технических рисков и слепых зон.
Развёрнутый ответ
Я строю стратегию тестирования от рисков продукта и архитектуры системы.
Сначала я разбираю, какие части системы являются критическими: где деньги,
данные, безопасность, права доступа, пользовательские сценарии,
интеграции, миграции, очереди, внешние API. После этого определяю, какие
ошибки будут самыми дорогими для бизнеса и команды. Именно эти зоны должны
быть покрыты в первую очередь.Дальше я разделяю тестирование по уровням.
На уровне unit-тестов мы закрываем бизнес-логику: расчёты, валидации,
правила, state transitions, маппинг, обработку ошибок. Эти тесты должны
быть быстрыми, стабильными и запускаться постоянно.На уровне integration-тестов мы проверяем реальные границы системы: работу
с базой данных, транзакции, миграции, очереди, кеш, внешние сервисы,
авторизацию, сериализацию, конфигурацию. Здесь важно поймать ошибки, которые
не видны в изолированной логике.На уровне contract tests мы фиксируем договорённости между сервисами:
форматы API, схемы событий, обязательные поля, статус-коды, backward
compatibility. Это особенно важно в микросервисах, потому что один сервис
может измениться и незаметно сломать другого потребителя.На уровне end-to-end-тестов мы покрываем только ключевые бизнес-сценарии.
Я не пытаюсь проверить через e2e всю систему, потому что такие тесты
дорогие, медленные и часто нестабильные. Но критические user journeys
должны быть защищены: регистрация, логин, создание ключевой сущности,
оплата, оформление заказа, обработка заявки, отправка уведомлений — в
зависимости от продукта.Отдельно я бы включил в стратегию non-functional testing: performance tests
для критических нагрузочных сценариев, security checks для авторизации и
доступа к данным, smoke tests после деплоя, regression suite перед релизом,
мониторинг и observability в production. Потому что качество системы — это
не только “тесты прошли”, но и способность быстро увидеть проблему после
релиза.Также я смотрю на pipeline. Тесты должны быть встроены в CI/CD: быстрые
проверки на pull request, более тяжёлые integration и e2e проверки на merge
или nightly run, smoke tests после деплоя. Важно, чтобы feedback был
быстрым, иначе команда начнёт обходить тестовую систему.Как лид я бы регулярно пересматривал стратегию тестирования. Если
появляются flaky tests, частые production bugs, долгий pipeline, слабое
покрытие критической логики или повторяющиеся регрессии — это сигнал, что
стратегию нужно менять. Тестирование должно развиваться вместе с
архитектурой и продуктом.Мой принцип: тестировать не всё подряд, а осознанно закрывать риски.
Хорошая стратегия тестирования даёт команде уверенность быстро менять
систему, не ломая критическое поведение.
Короткая формулировка
Я строю стратегию тестирования от рисков: сначала определяю критические
зоны продукта и архитектуры, затем покрываю их правильным уровнем тестов —
unit для логики, integration для границ системы, contract tests для
взаимодействия сервисов и ограниченный набор e2e для ключевых
бизнес-сценариев.