Spring Boot Code Coverage: Complete Guide (2025)


Spring Boot Code Coverage

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.

Pro Tip: Spring Boot's testing framework provides powerful features for achieving high code coverage. Use @SpringBootTest for integration tests and @WebMvcTest for controller tests.

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:

  1. Add the rules configuration to your pom.xml
  2. Run Maven with the verify goal:
    mvn clean verify
  3. Check the generated report in target/site/jacoco/index.html
Pro Tip: You can define different rules for different packages or classes using the <element> tag with specific patterns.

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.