1. Types of Tests
- Unit tests → check individual classes (fast, isolated).
- Integration tests → check component interaction (e.g., service + DB).
- End-to-End (E2E) → check the API as a whole (through MockMvc/TestRestTemplate).
2. Unit Tests (JUnit 5)
📄 Dependency (usually already present in spring-boot-starter-test):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>📄 Example:
class Calculator {
int sum(int a, int b) { return a + b; }
}
class CalculatorTest {
Calculator calc = new Calculator();
@Test
void testSum() {
assertEquals(5, calc.sum(2, 3));
}
}3. Mockito (mocks and stubs)
📄 Example:
@Service
class UserService {
private final UserRepository repo;
public UserService(UserRepository repo) { this.repo = repo; }
User getById(Long id) { return repo.findById(id).orElseThrow(); }
}
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock UserRepository repo;
@InjectMocks UserService service;
@Test
void testGetById() {
User u = new User(); u.setName("Vitaliy");
when(repo.findById(1L)).thenReturn(Optional.of(u));
User result = service.getById(1L);
assertEquals("Vitaliy", result.getName());
}
}4. Spring Boot Integration Test
📄 Example:
@SpringBootTest
class UserServiceIntegrationTest {
@Autowired UserRepository repo;
@Autowired UserService service;
@Test
void testCreateUser() {
User u = new User();
u.setName("Test");
service.createUser(u);
assertThat(repo.findByName("Test")).isNotEmpty();
}
}📌 The @SpringBootTest annotation loads the entire Spring context (slower, but closest to reality).
5. MockMvc (testing REST API)
📄 Example:
@WebMvcTest(UserController.class)
class UserControllerTest {
@Autowired private MockMvc mockMvc;
@MockBean private UserService service;
@Test
void testGetUsers() throws Exception {
when(service.allUsers()).thenReturn(List.of(new UserDto(1L, "Vitaliy")));
mockMvc.perform(get("/api/users"))
.andExpect(status().isOk())
.andExpect(jsonPath("$[0].name").value("Vitaliy"));
}
}📌 @WebMvcTest loads only the web layer (without the DB). @MockBean replaces the service with a mock.
6. TestRestTemplate (integration API tests)
📄 Example:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class UserApiTest {
@Autowired TestRestTemplate restTemplate;
@Test
void testCreateUser() {
UserDto dto = new UserDto(null, "Vitaliy");
ResponseEntity<UserDto> response =
restTemplate.postForEntity("/api/users", dto, UserDto.class);
assertEquals(HttpStatus.OK, response.getStatusCode());
assertEquals("Vitaliy", response.getBody().name());
}
}7. Testcontainers (with a real DB)
📄 Example:
@SpringBootTest
@Testcontainers
class UserRepositoryTest {
@Container
static PostgreSQLContainer<?> postgres =
new PostgreSQLContainer<>("postgres:15")
.withDatabaseName("demo")
.withUsername("demo")
.withPassword("secret");
@DynamicPropertySource
static void props(DynamicPropertyRegistry reg) {
reg.add("spring.datasource.url", postgres::getJdbcUrl);
reg.add("spring.datasource.username", postgres::getUsername);
reg.add("spring.datasource.password", postgres::getPassword);
}
@Autowired UserRepository repo;
@Test
void testSaveUser() {
User u = new User();
u.setName("Vitaliy");
repo.save(u);
assertThat(repo.findByName("Vitaliy")).isNotEmpty();
}
}📌 These are the most realistic integration tests.
8. H2 (In-memory DB)
Quick replacement for dev/test. 📄 Setup:
spring:
datasource:
url: jdbc:h2:mem:testdb
driver-class-name: org.h2.Driver
username: sa
password: password
h2:
console:
enabled: true📌 Open H2 console: http://localhost:8080/h2-console
9. Test Utilities
- @DataJpaTest → tests only the JPA layer.
- @JsonTest → tests Jackson serialization/deserialization.
- @WebMvcTest → only controllers.
- @SpringBootTest → entire context.
10. Best Practices
- ✅ Write unit tests for business logic.
- ✅ Integration tests → for DB and API.
- ✅ Use MockMvc for REST, don’t load the entire server.
- ✅ For complex cases → Testcontainers (real DB in Docker).
- ✅ Tests should be fast and isolated.
- ✅ Test levels:
- Unit → cheap and fast,
- Integration → more expensive,
- E2E → only for critical scenarios.