Spring Boot Code Coverage: Complete Guide (2025)

Code coverage in Spring Boot applications requires a comprehensive approach that combines unit testing, integration testing, and proper tooling. This guide explores how to achieve and maintain high code coverage in Spring Boot projects.
Table of Contents
1. Introduction to Spring Boot Testing
Spring Boot provides a comprehensive testing framework that makes it easier to achieve high code coverage. The framework includes:
- Built-in test support with @SpringBootTest
- MockMvc for testing web layers
- TestRestTemplate for integration testing
- Support for various testing libraries
- Easy configuration of test properties
2. Types of Testing in Spring Boot
Spring Boot applications require different types of testing to achieve comprehensive coverage:
2.1 Unit Testing
Unit testing in Spring Boot focuses on testing individual components in isolation:
@SpringBootTest
class UserServiceTest {
@Autowired
private UserService userService;
@MockBean
private UserRepository userRepository;
@Test
void createUser_WithValidData_ShouldSucceed() {
// Arrange
UserDTO userDTO = new UserDTO("John Doe", "john@example.com");
User expectedUser = new User("John Doe", "john@example.com");
when(userRepository.save(any(User.class))).thenReturn(expectedUser);
// Act
User result = userService.createUser(userDTO);
// Assert
assertNotNull(result);
assertEquals("John Doe", result.getName());
assertEquals("john@example.com", result.getEmail());
}
}
2.2 Integration Testing
Integration testing verifies the interaction between different components:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class UserControllerIntegrationTest {
@Autowired
private TestRestTemplate restTemplate;
@Test
void createUser_WithValidData_ShouldReturnCreated() {
// Arrange
UserDTO userDTO = new UserDTO("John Doe", "john@example.com");
// Act
ResponseEntity response = restTemplate.postForEntity(
"/api/users",
userDTO,
User.class
);
// Assert
assertEquals(HttpStatus.CREATED, response.getStatusCode());
assertNotNull(response.getBody());
}
}
3. JaCoCo Setup in Spring Boot
JaCoCo is the recommended code coverage tool for Spring Boot applications. Here's how to set it up:
3.1 Maven Configuration
Add the JaCoCo Maven plugin to your pom.xml file:
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.7</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
3.2 Coverage Rules
JaCoCo allows you to define coverage rules to enforce minimum coverage requirements. Here's how to configure coverage rules:
<rules>
<rule>
<element>BUNDLE</element>
<limits>
<limit>
<counter>LINE</counter>
<value>COVEREDRATIO</value>
<minimum>0.80</minimum>
</limit>
</limits>
</rule>
</rules>
Understanding Coverage Rules
Let's break down the coverage rules configuration:
- BUNDLE: Applies the rule to the entire project bundle
- COUNTER: Specifies what to measure:
- LINE: Line coverage
- BRANCH: Branch coverage
- METHOD: Method coverage
- CLASS: Class coverage
- VALUE: Defines how to evaluate the coverage:
- COVEREDRATIO: Ratio of covered to total items
- MISSED: Number of missed items
- COVERED: Number of covered items
- MINIMUM: Sets the minimum required coverage (0.80 = 80%)
Applying Coverage Rules
To apply coverage rules in your Spring Boot project:
- Add the rules configuration to your pom.xml
- Run Maven with the verify goal:
mvn clean verify
- Check the generated report in target/site/jacoco/index.html
Example: Package-Specific Rules
<rules>
<rule>
<element>PACKAGE</element>
<includes>com.example.service.*</includes>
<limits>
<limit>
<counter>LINE</counter>
<value>COVEREDRATIO</value>
<minimum>0.90</minimum>
</limit>
</limits>
</rule>
<rule>
<element>PACKAGE</element>
<includes>com.example.controller.*</includes>
<limits>
<limit>
<counter>LINE</counter>
<value>COVEREDRATIO</value>
<minimum>0.85</minimum>
</limit>
</limits>
</rule>
</rules>
4. Testing Strategies
Effective testing strategies for Spring Boot applications include:
4.1 Controller Testing
@WebMvcTest(UserController.class)
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
void createUser_WithValidData_ShouldReturnCreated() throws Exception {
// Arrange
UserDTO userDTO = new UserDTO("John Doe", "john@example.com");
// Act & Assert
mockMvc.perform(post("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(userDTO)))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.name").value("John Doe"));
}
}
4.2 Service Layer Testing
@SpringBootTest
class OrderServiceTest {
@Autowired
private OrderService orderService;
@MockBean
private PaymentService paymentService;
@Test
void processOrder_WithValidPayment_ShouldSucceed() {
// Arrange
Order order = new Order(100.0);
when(paymentService.processPayment(any())).thenReturn(true);
// Act
Order result = orderService.processOrder(order);
// Assert
assertTrue(result.isPaid());
verify(paymentService).processPayment(any());
}
}
5. Best Practices
Follow these best practices to maintain high code coverage in Spring Boot applications:
- Use appropriate test annotations (@SpringBootTest, @WebMvcTest, @DataJpaTest)
- Implement proper test isolation
- Use meaningful test names
- Test both success and failure scenarios
- Mock external dependencies
- Use test containers for database testing
- Implement proper cleanup after tests
- Use test profiles for different environments
6. CI/CD Integration
Integrate code coverage reporting into your CI/CD pipeline:
6.1 Jenkins Pipeline
pipeline {
agent any
stages {
stage('Build and Test') {
steps {
sh 'mvn clean verify'
jacoco(
execPattern: '**/target/coverage.exec',
classPattern: '**/target/classes',
sourcePattern: '**/src/main/java',
exclusionPattern: '**/test/**'
)
}
}
}
}
7. Advanced Coverage Scenarios
Let's explore some advanced scenarios and how to achieve coverage for them:
7.1 Testing Exception Scenarios
@SpringBootTest
class PaymentServiceTest {
@Autowired
private PaymentService paymentService;
@Test
void processPayment_WithInvalidAmount_ShouldThrowException() {
// Arrange
Payment payment = new Payment(-100.0);
// Act & Assert
assertThrows(IllegalArgumentException.class, () -> {
paymentService.processPayment(payment);
});
}
}
7.2 Testing Async Operations
@SpringBootTest
class AsyncServiceTest {
@Autowired
private AsyncService asyncService;
@Test
void processAsyncTask_ShouldCompleteSuccessfully() throws Exception {
// Arrange
CompletableFuture future = asyncService.processAsyncTask("test");
// Act & Assert
assertEquals("Processed: test", future.get(5, TimeUnit.SECONDS));
}
}
8. Coverage for Different Layers
Understanding how to achieve coverage for different application layers:
8.1 Repository Layer Coverage
@DataJpaTest
class UserRepositoryTest {
@Autowired
private UserRepository userRepository;
@Test
void findByEmail_WithExistingUser_ShouldReturnUser() {
// Arrange
User user = new User("John", "john@example.com");
userRepository.save(user);
// Act
Optional found = userRepository.findByEmail("john@example.com");
// Assert
assertTrue(found.isPresent());
assertEquals("John", found.get().getName());
}
}
8.2 Security Layer Coverage
@WebMvcTest(SecurityConfig.class)
class SecurityConfigTest {
@Autowired
private MockMvc mockMvc;
@Test
void secureEndpoint_WithValidToken_ShouldAllowAccess() throws Exception {
// Arrange
String token = generateValidToken();
// Act & Assert
mockMvc.perform(get("/api/secure")
.header("Authorization", "Bearer " + token))
.andExpect(status().isOk());
}
}
9. Performance Testing Coverage
Measuring coverage for performance-critical code:
9.1 Cache Coverage
@SpringBootTest
class CacheServiceTest {
@Autowired
private CacheService cacheService;
@Test
void getCachedData_ShouldReturnFromCache() {
// Arrange
String key = "test-key";
String value = "test-value";
cacheService.put(key, value);
// Act
String result = cacheService.get(key);
// Assert
assertEquals(value, result);
verify(cacheService, times(1)).getFromSource(key);
}
}
9.2 Batch Processing Coverage
@SpringBatchTest
class BatchJobTest {
@Autowired
private JobLauncherTestUtils jobLauncherTestUtils;
@Test
void batchJob_ShouldProcessAllItems() throws Exception {
// Act
JobExecution jobExecution = jobLauncherTestUtils.launchJob();
// Assert
assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());
assertEquals(100, jobExecution.getStepExecutions().iterator().next().getWriteCount());
}
}
10. Coverage Reporting and Analysis
Understanding and analyzing coverage reports:
10.1 JaCoCo Report Analysis
- Line coverage analysis
- Branch coverage analysis
- Method coverage analysis
- Class coverage analysis
- Package coverage analysis
10.2 Coverage Thresholds
<rules>
<rule>
<element>BUNDLE</element>
<limits>
<limit>
<counter>LINE</counter>
<value>COVEREDRATIO</value>
<minimum>0.80</minimum>
</limit>
<limit>
<counter>BRANCH</counter>
<value>COVEREDRATIO</value>
<minimum>0.70</minimum>
</limit>
</limits>
</rule>
</rules>
Conclusion
Maintaining high code coverage in Spring Boot applications requires a combination of proper testing strategies, tools, and best practices. By following the guidelines in this article and using the appropriate testing frameworks and tools, you can ensure your Spring Boot applications are well-tested and maintainable.