Spring BootMiddleTechnical

Какова роль @SpringBootTest в интеграционном тестировании?

@SpringBootTest поднимает полный Spring ApplicationContext для интеграционных тестов. Поддерживает режимы MOCK (MockMvc), RANDOM_PORT (реальный HTTP) и NONE (только контекст без веб-слоя).

@SpringBootTest в интеграционном тестировании

@SpringBootTest — аннотация Spring Test, которая поднимает полный (или частичный) контекст Spring ApplicationContext для интеграционных тестов. В отличие от юнит-тестов с @ExtendWith(MockitoExtension.class), здесь участвуют реальная автоконфигурация, бины, база данных и HTTP-слой.

Режимы webEnvironment

  • MOCK (по умолчанию) — создаёт mock-среду веб-контейнера, используется с MockMvc.
  • RANDOM_PORT — запускает реальный встроенный сервер на случайном порту; используется с TestRestTemplate или WebTestClient.
  • DEFINED_PORT — реальный сервер на порту из application.properties.
  • NONE — без веб-среды, только ApplicationContext.

Пример: тест с MockMvc

@SpringBootTest
@AutoConfigureMockMvc
@ActiveProfiles("test")
class UserControllerIntegrationTest {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private UserRepository userRepository;

    @BeforeEach
    void setUp() {
        userRepository.deleteAll();
        userRepository.save(new User("Alice", "alice@example.com"));
    }

    @Test
    void shouldReturnUserList() throws Exception {
        mockMvc.perform(get("/api/users")
                .contentType(MediaType.APPLICATION_JSON))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$[0].name").value("Alice"));
    }

    @Test
    void shouldCreateUser() throws Exception {
        String requestBody = """
            {"name": "Bob", "email": "bob@example.com"}
            """;

        mockMvc.perform(post("/api/users")
                .contentType(MediaType.APPLICATION_JSON)
                .content(requestBody))
            .andExpect(status().isCreated())
            .andExpect(jsonPath("$.name").value("Bob"));
    }
}

Пример: тест с реальным HTTP (RANDOM_PORT)

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("test")
class OrderApiIntegrationTest {

    @Autowired
    private TestRestTemplate restTemplate;

    @LocalServerPort
    private int port;

    @Test
    void shouldReturnOrderById() {
        ResponseEntity<OrderDto> response = restTemplate
            .getForEntity("/api/orders/1", OrderDto.class);

        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
        assertThat(response.getBody()).isNotNull();
    }
}

Изоляция через @Transactional и @Sql

@SpringBootTest
@Transactional // откатывает каждый тест
@Sql("/sql/test-data.sql") // вставляет данные перед тестом
class ProductServiceIntegrationTest {

    @Autowired
    private ProductService productService;

    @Test
    void shouldFindProductByCategory() {
        List<Product> products = productService.findByCategory("electronics");
        assertThat(products).hasSize(3);
    }
}

Мокирование отдельных бинов

@SpringBootTest
class PaymentIntegrationTest {

    @MockBean // заменяет реальный бин в контексте Spring моком Mockito
    private PaymentGateway paymentGateway;

    @Autowired
    private OrderService orderService;

    @Test
    void shouldProcessPayment() {
        when(paymentGateway.charge(any())).thenReturn(PaymentResult.success("txn-123"));

        OrderResult result = orderService.placeOrder(new OrderRequest(...));

        assertThat(result.isPaid()).isTrue();
        verify(paymentGateway).charge(any());
    }
}

Кеширование контекста

Spring Test кеширует ApplicationContext между тестовыми классами с одинаковой конфигурацией. Использование @MockBean или @DirtiesContext создаёт новый контекст, что замедляет тесты. Группируйте тесты с одинаковым набором моков.

Подводные камни

  • @SpringBootTest поднимает весь контекст — тесты становятся медленными. Для тестирования одного слоя используйте слайсы: @WebMvcTest, @DataJpaTest, @JsonTest.
  • @MockBean инвалидирует кеш контекста — каждый класс с уникальным набором @MockBean создаёт новый контекст. Сведите число комбинаций к минимуму.
  • Использование RANDOM_PORT с @Transactional не откатывает транзакции — HTTP-запросы идут через реальный контейнер в другом потоке.
  • Без @ActiveProfiles("test") тест может подключиться к production-базе, если spring.profiles.active не задан явно.
  • Не чистить БД между тестами — состояние из одного теста ломает другой. Используйте @Transactional, @Sql или явный @BeforeEach.
  • @DirtiesContext на каждом тестовом классе сбрасывает кеш и многократно поднимает контекст — тестовый прогон превращается в многоминутный.
  • Забыть добавить spring-boot-starter-test в зависимости — тест не скомпилируется без JUnit 5 и Spring Test.

Common mistakes

  • Путать термин «springboottest» с соседним механизмом Spring Boot.
  • Не называть границу lifecycle, transaction, thread или request для «springboottest».
  • Игнорировать production-эффекты «springboottest»: latency, SQL shape, memory, security или observability.

What the interviewer is testing

  • Попросить объяснить механизм «springboottest» на минимальном примере.
  • Проверить, видит ли кандидат failure mode и диагностику для «springboottest».
  • Уточнить, какие настройки или API меняют «springboottest» в реальном сервисе.

Sources

Related topics