Skip to content

Spring Testing

Spring Boot provides excellent testing support. Use @SpringBootTest for integration tests (loads full context), @WebMvcTest for controller-only tests (MockMvc), @DataJpaTest for repository tests (embedded DB). Use Mockito to mock dependencies. TestContainers enables testing with real databases. The testing pyramid: many unit tests, some integration tests, few end-to-end tests.

         ┌─────────┐
         │  E2E    │  Few — slow, expensive
         │  Tests  │
        ─┼─────────┼─
        │Integration│  Some — test Spring context
        │  Tests    │
       ─┼───────────┼─
       │  Unit Tests │  Many — fast, isolated
       └─────────────┘

Unit Tests with Mockito

Unit tests test a single class in isolation — mock all dependencies with Mockito. Use @ExtendWith(MockitoExtension.class), @Mock for dependencies, @InjectMocks for class under test. Pattern: Given (setup mocks) → When (call method) → Then (assert + verify). Fast, no Spring context needed.

Deep Dive: Example & Annotations
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {
    @Mock private OrderRepository orderRepository;
    @Mock private PaymentService paymentService;
    @InjectMocks private OrderService orderService;

    @Test
    void shouldCreateOrder() {
        // Given
        when(orderRepository.save(any(Order.class))).thenReturn(savedOrder);
        when(paymentService.charge(anyDouble())).thenReturn(true);

        // When
        Order result = orderService.createOrder(request);

        // Then
        assertThat(result.getId()).isEqualTo(1L);
        verify(orderRepository).save(any(Order.class));
        verify(paymentService).charge(20.0);
    }
}
Annotation Purpose
@Mock Create mock object
@InjectMocks Inject mocks into class under test
@Spy Partial mock (real methods unless stubbed)
@Captor Capture arguments passed to mocks

@WebMvcTest (Controller Tests)

Tests only the web layer — controllers, filters, advice. No services, repos, or DB. Uses MockMvc to perform HTTP requests and assert responses. Mock service-layer beans with @MockBean. Fast because it loads only web-related beans.

Deep Dive: Example
@WebMvcTest(UserController.class)
class UserControllerTest {
    @Autowired private MockMvc mockMvc;
    @MockBean private UserService userService;

    @Test
    void shouldReturnUser() throws Exception {
        when(userService.findById(1L)).thenReturn(new UserResponse(1L, "John", "john@test.com"));

        mockMvc.perform(get("/api/users/1")
                .contentType(MediaType.APPLICATION_JSON))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.name").value("John"))
            .andExpect(jsonPath("$.email").value("john@test.com"));
    }

    @Test
    void shouldValidateInput() throws Exception {
        mockMvc.perform(post("/api/users")
                .contentType(MediaType.APPLICATION_JSON)
                .content("""{"name": "", "email": "not-an-email"}"""))
            .andExpect(status().isBadRequest());
    }
}

@DataJpaTest (Repository Tests)

Tests JPA repositories with an embedded database (H2). Auto-configures EntityManager, repositories, and transaction rollback. Use TestEntityManager for test data setup. Fast, focused on data access layer only.

Deep Dive: Example
@DataJpaTest
class UserRepositoryTest {
    @Autowired private UserRepository userRepository;
    @Autowired private TestEntityManager entityManager;

    @Test
    void shouldFindByEmail() {
        entityManager.persistAndFlush(new User("John", "john@test.com"));

        Optional<User> found = userRepository.findByEmail("john@test.com");

        assertThat(found).isPresent();
        assertThat(found.get().getName()).isEqualTo("John");
    }
}

@SpringBootTest (Integration Tests)

Loads the full Spring context. Use for end-to-end testing with TestRestTemplate or WebTestClient. WebEnvironment.RANDOM_PORT starts a real server. Slowest but most realistic. Use sparingly — rely on unit and slice tests for speed.

Deep Dive: Example & Web Environments
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class OrderIntegrationTest {
    @Autowired private TestRestTemplate restTemplate;

    @Test
    void shouldCreateAndRetrieveOrder() {
        ResponseEntity<Order> response = restTemplate.postForEntity(
            "/api/orders", request, Order.class);
        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.CREATED);
    }
}
WebEnvironment Description
MOCK (default) Mock servlet (use MockMvc)
RANDOM_PORT Real server on random port
DEFINED_PORT Real server on configured port
NONE No web environment

TestContainers

Test with real databases (PostgreSQL, MySQL, Redis, etc.) instead of H2. Uses Docker containers that start/stop automatically with tests. Configure via @Container and @DynamicPropertySource. Ensures tests match production database behavior.

Deep Dive: Example
@SpringBootTest
@Testcontainers
class UserRepositoryContainerTest {
    @Container
    static PostgreSQLContainer<?> postgres =
        new PostgreSQLContainer<>("postgres:15")
            .withDatabaseName("testdb");

    @DynamicPropertySource
    static void configureProperties(DynamicPropertyRegistry registry) {
        registry.add("spring.datasource.url", postgres::getJdbcUrl);
        registry.add("spring.datasource.username", postgres::getUsername);
        registry.add("spring.datasource.password", postgres::getPassword);
    }

    @Autowired private UserRepository userRepository;

    @Test
    void shouldWorkWithRealPostgres() {
        User user = userRepository.save(new User("John", "john@test.com"));
        assertThat(user.getId()).isNotNull();
    }
}

Testing Best Practices

  1. Use AssertJ for readable assertions. 2. Use @MockBean sparingly (slows context). 3. Use @ActiveProfiles("test") for test config. 4. Use @Sql for test data. 5. Follow test pyramid: many unit tests, slice tests, few integration tests. Slice annotations: @WebMvcTest, @DataJpaTest, @WebFluxTest, @JsonTest, @RestClientTest.
Deep Dive: Slice Test Annotations
Annotation What It Loads
@WebMvcTest Controllers, filters, advice only
@DataJpaTest JPA repos, EntityManager, DataSource
@WebFluxTest WebFlux controllers
@JsonTest Jackson ObjectMapper
@RestClientTest RestTemplate/WebClient

Common Interview Questions

Common Interview Questions
  • What is the difference between @SpringBootTest and @WebMvcTest?
  • What is MockMvc? How do you use it?
  • What is the difference between @Mock and @MockBean?
  • When would you use @DataJpaTest?
  • What is TestContainers? When would you use it?
  • What is the testing pyramid?
  • How do you test REST APIs in Spring Boot?
  • What are slice test annotations?