Вопросы по тестированию на Java на собеседовании
Умение писать тесты — быстрый способ отличить джуна от мидла, поэтому блок про тестирование почти всегда есть. Джун показывает синтаксис @Test и знание ассертов, мидл объясняет, почему тест не должен зависеть от порядка запуска, а сюита из сотен @SpringBootTest ползёт по десять минут.
Что спрашивают
- +JUnit 5: жизненный цикл и порядок аннотаций, @ParameterizedTest, вложенные тесты, чем assertThrows лучше try-catch
- +Mockito: mock против spy, стабирование против verify, чем @Mock отличается от @MockBean, когда мок вредит
- +Spring Test: цена полного контекста, слайсы @WebMvcTest и @DataJpaTest, кеш контекстов и что его ломает
- +Интеграция: Testcontainers вместо H2, реальная схема и миграции, переиспользование контейнера между тестами
- +Практики: TDD и порядок «красный-зелёный-рефакторинг», почему покрытие не цель, хрупкие тесты и тесты-на-реализацию
Из чего состоит тема
Так тема разложена в тренажёре: движок ведёт прогресс по каждой подтеме отдельно и возвращает те, где вы ошибаетесь.
- JUnit 5: основы15
- Mockito и моки14
- Spring Test и слайсы14
- TDD и практики14
- Интеграционные тесты14
Разборы подтем
Конспект по каждой: что это, как отвечать вслух, на чём валятся, плюс вопросы для самопроверки.
- JUnit 5: основы15 вопросов
- Mockito: моки и стабы14 вопросов
- Spring Test: контекст и слайсы14 вопросов
- Интеграционные тесты и Testcontainers14 вопросов
- TDD и практики тестирования14 вопросов
Примеры вопросов с разбором
- Что описывает пирамида тестов?A)Много медленных e2e сверху, немного юнитов внизуB)Поровну юнитов, интеграционных и e2e-тестов на каждом уровне этой пирамидыC)Только юниты — остальные типы избыточныD)Много быстрых юнитов внизу, меньше интеграционных, единицы e2e сверху
показать ответ и разбор
+D)Много быстрых юнитов внизу, меньше интеграционных, единицы e2e сверху// разбор: Пирамида: широкое основание из быстрых изолированных юнитов, средний слой интеграционных (слой + реальная зависимость), узкая верхушка дорогих медленных e2e. Форма отражает баланс скорости и уверенности: основную массу багов ловят дёшево внизу, а сквозные сценарии проверяют точечно наверху. Перевёрнутая пирамида (много e2e) — медленно и хрупко.
- Чем @BeforeEach отличается от @BeforeAll в JUnit 5?A)@BeforeEach запускается перед каждым тестом, @BeforeAll — один раз перед всеми тестами классаB)@BeforeEach выполняется только для параметризованных тестов, @BeforeAll — для обычныхC)@BeforeEach вызывается после теста, @BeforeAll — до; порядок задаётся аннотацией @Order над методамиD)Разницы нет, это синонимы из разных версий JUnit, оставленные для совместимости
показать ответ и разбор
+A)@BeforeEach запускается перед каждым тестом, @BeforeAll — один раз перед всеми тестами класса// разбор: @BeforeEach исполняется перед КАЖДЫМ тест-методом — там готовят свежее состояние (новый объект, сброшенные моки). @BeforeAll — один раз на весь класс до всех тестов, для дорогой общей подготовки; поэтому он static (без @TestInstance(PER_CLASS)). Симметрично работают @AfterEach/@AfterAll.
- Что возвращают методы мока, созданного mock(Repo.class), до какого-либо стабинга?A)Бросают UnsupportedOperationException уже на первом вызовеB)Значения по умолчанию: null для объектов, 0 для чисел, пустые коллекцииC)Вызывают реальную реализацию RepoD)Возвращают случайные значения, чтобы заодно проверить устойчивость кода
показать ответ и разбор
+B)Значения по умолчанию: null для объектов, 0 для чисел, пустые коллекции// разбор: Незастабленный мок отвечает дефолтами: null для ссылочных типов, 0/false для примитивов, пустые коллекции (не null) для List/Map/Set. Реальный код не вызывается — на то он и мок. Отсюда частый NPE в тесте: метод вернул null, потому что его забыли застабить через when().thenReturn().
- Что делает аннотация @SpringBootTest на тест-классе?A)Создаёт голый экземпляр класса вообще без какого-либо Spring-контекстаB)Загружает только web-слой с контроллерами и подменяет сервисы мокамиC)Поднимает полный контекст приложения Spring для интеграционного тестаD)Запускает приложение в отдельном процессе и дёргает его по сети
показать ответ и разбор
+C)Поднимает полный контекст приложения Spring для интеграционного теста// разбор: @SpringBootTest поднимает весь контекст приложения (все бины, конфигурацию) — это полноценный интеграционный тест, и потому медленный. Web-слой в изоляции — это @WebMvcTest, а не он. По умолчанию сервер не стартует (webEnvironment=MOCK); реальный порт включают RANDOM_PORT.
- Какой порядок шагов в цикле TDD?A)Red → green → refactor: падающий тест, минимум кода, затем чисткаB)Refactor → red → green: сначала рефакторим, потом пишем падающий тестC)Green → red → refactor: сперва рабочий код, потом тестD)Написать весь код, затем все тесты, затем рефакторинг
показать ответ и разбор
+A)Red → green → refactor: падающий тест, минимум кода, затем чистка// разбор: TDD идёт циклом red-green-refactor: сначала падающий тест, фиксирующий намерение (red), затем минимум кода, чтобы он позеленел (green), затем чистка кода при зелёных тестах (refactor). Тест первым описывает поведение и служит сетью безопасности для последующего рефакторинга. Код-до-теста — это уже не TDD.
- Что делает Testcontainers в интеграционном тесте?A)Поднимает реальную зависимость (Postgres, Kafka) в Docker на время тестаB)Подменяет базу на встроенную H2 в памяти процессаC)Генерирует моки для внешних сервисов по их OpenAPID)Запускает сами тесты в изолированных Docker-контейнерах вместо локальной JVM
показать ответ и разбор
+A)Поднимает реальную зависимость (Postgres, Kafka) в Docker на время теста// разбор: Testcontainers стартует настоящий контейнер (Postgres, Kafka, Redis и т.п.) в Docker на время теста и гасит его после — тест работает с тем же движком, что и прод, вместо суррогата H2. Это убирает разрыв «диалекты не совпали». Моки по OpenAPI — не про него, и тесты по-прежнему исполняются в JVM.
- Зачем нужен assertThrows вместо ручного try/catch с fail()?A)assertThrows работает быстрее, потому что не создаёт объект исключенияB)Он лаконично проверяет, что код бросил нужное исключение, и возвращает его для проверки сообщенияC)assertThrows — обязательный способ поймать checked-исключение в тестеD)Он подавляет исключения теста, оставляя его зелёным
показать ответ и разбор
+B)Он лаконично проверяет, что код бросил нужное исключение, и возвращает его для проверки сообщения// разбор: assertThrows(Ex.class, () -> code()) проверяет, что лямбда бросила именно это исключение, и возвращает пойманный объект — можно заассертить его message или причину. Ручной try/catch с fail() многословен и коварен: забыл fail() после вызова — тест молча зелёный, даже если исключения не было.
- Как задать, что mock.findById(1) вернёт конкретный объект?A)mock.findById(1).set(user)B)verify(mock.findById(1)).thenReturn(user)C)when(mock.findById(1)).thenReturn(user)D)assertReturns(mock.findById(1), user)
показать ответ и разбор
+C)when(mock.findById(1)).thenReturn(user)// разбор: Стабинг задаётся связкой when(...).thenReturn(...): при вызове findById(1) мок отдаст user. Для исключения — thenThrow. verify — про проверку факта вызова, а не про задание ответа; assert — про утверждения о результате. Аргумент можно обобщить матчером any() или eq(1).
- Что грузит слайс @WebMvcTest(OrderController.class)?A)Всю схему БД вместе с репозиториями и полным entity-менеджером JPAB)Ничего из Spring — это чистый юнит-тест контроллераC)Полный контекст, но с отключённой безопасностьюD)Только web-слой: сам контроллер, MockMvc и инфраструктуру MVC
показать ответ и разбор
+D)Только web-слой: сам контроллер, MockMvc и инфраструктуру MVC// разбор: @WebMvcTest поднимает узкий срез — MVC-инфраструктуру и указанный контроллер, плюс готовый MockMvc. Сервисы и репозитории в контекст не входят: их зависимости подставляют как @MockBean. Сервер не стартует. Быстрее @SpringBootTest, потому что грузит один слой, а не всё приложение.
это 9 из 71
Ещё 62 вопросов по теме — в тренажёре, с движком повторения
Прочитать разбор и ответить самому — разные навыки. В Сеньорчике вопросы идут сессиями, а движок возвращает подтемы, где вы ошибаетесь, пока они не начнут отскакивать. Бесплатно, лимит по энергии.
Частые вопросы
Чем @Mock отличается от @MockBean?
@Mock — это чистый Mockito, объект создаётся без Spring и подставляется руками или через @InjectMocks. @MockBean подменяет бин в контексте приложения, поэтому тянет за собой поднятие контекста и его пересоздание: каждый новый набор @MockBean — это новый закешированный контекст и лишние секунды на сюите.
Почему H2 хуже Testcontainers?
H2 — другая СУБД с другим диалектом: она молча проглатывает то, на чём упадёт PostgreSQL, и наоборот. Тест на H2 проверяет код против чужого поведения, поэтому реальную схему, типы и миграции ловят только на настоящей базе, поднятой контейнером.
Какое покрытие считается достаточным?
Числа сами по себе ничего не значат: сотня процентов достигается тестами без единого ассерта. На собесе ждут, что вы говорите о покрытии ветвлений в сложной логике и о том, какие места намеренно оставлены без тестов, а не называете цифру из регламента.