Detailed Description
When attempting to write an integration test using the standard Spring Boot testing stack (spring-boot-starter-test), the @SpyBean annotation (from org.springframework.boot.test.mock.mockito) is reported as "cannot resolve symbol" or results in a compilation failure, indicating that the class definition for @SpyBean is either missing or inaccessible, despite the correct dependencies being present.
The test class is correctly configured for JUnit 5 (Jupiter) (no @RunWith annotation is used). This issue suggests a packaging or dependency resolution problem specifically within the spring-boot-test-autoconfigure module in Spring Boot 4.0.0 GA, or a potential conflict with the new modular structure.
This failure prevents standard mocking and spying in Spring Boot 4 integration tests.
Environment Details (Assumed)
The issue is reproducible with the following environment configuration:
- Spring Boot Version: 4.0.0 (GA)
- Spring Framework Version: 7.0.1 (GA)
- Java Version: OpenJDK 21
- Build Tool: Apache Maven 3.9.9
- Operating System: Windows 10
Steps to Reproduce
- Create a standard Spring Boot 4 Maven project structure.
- Use the
pom.xmlprovided below. - Add the
ScheduledService.javacomponent (provided below). - Add the
ScheduledServiceIntegrationTest.javatest file (provided below). - Attempt to compile or run the test from the terminal.
Command Line Steps
-
Execute a clean build and package:
mvn clean install -
Execute the test:
mvn test
Expected Result
The test should compile successfully, and mvn test should run the test, leading to a passing result.
Actual Result
Compilation fails with an error similar to:
[ERROR] COMPILATION ERROR :
[ERROR] /path/to/ScheduledServiceIntegrationTest.java:[8,46] cannot find symbol
symbol: class SpyBean
location: package org.springframework.boot.test.mock.mockito
Problematic Code
The following code is confirmed to be in use and exhibits the issue:
- pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="[http://maven.apache.org/POM/4.0.0](http://maven.apache.org/POM/4.0.0)"
xmlns:xsi="[http://www.w3.org/2001/XMLSchema-instance](http://www.w3.org/2001/XMLSchema-instance)"
xsi:schemaLocation="[http://maven.apache.org/POM/4.0.0](http://maven.apache.org/POM/4.0.0) [http://maven.apache.org/xsd/maven-4.0.0.xsd](http://maven.apache.org/xsd/maven-4.0.0.xsd)">
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>4.0.0</version>
<relativePath/>
</parent>
<!-- ... project coordinates ... -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- ... other dependencies ... -->
</dependencies>
<!-- ... build section ... -->
</project>
- src/main/java/com/baeldung/scheduling/ScheduledService.java
package com.example.scheduling;
import org.springframework.stereotype.Service;
/**
* Service to be spied on by the integration test.
* In a real application, this would contain complex logic.
*/
@Service
public class ScheduledService {
private int executionCount = 0;
// This is the method we want to verify was called by the scheduler.
public void executeScheduledTask() {
System.out.println("--- Executing scheduled task (Real Implementation) ---");
executionCount++;
// The actual business logic goes here
}
public int getExecutionCount() {
return executionCount;
}
}
- src/test/java/com/baeldung/scheduling/ScheduledServiceIntegrationTest.java
package com.example.scheduling;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.test.context.ActiveProfiles;
import static org.mockito.Mockito.verify;
import java.util.concurrent.TimeUnit;
// The @SpringBootTest annotation automatically enables the JUnit 5 SpringExtension.
@SpringBootTest
@ActiveProfiles("test") // Ensures the test runs in a controlled environment
public class ScheduledServiceIntegrationTest {
// The service that is the target of the spy
@SpyBean // <-- FAILURE POINT
private ScheduledService scheduledService;
// A dependency that triggers the method call we want to spy on
// Assuming this class triggers scheduledService.executeScheduledTask()
@Autowired
private SchedulerComponent scheduler;
// Note: You would likely need to configure a minimal @EnableScheduling setup
// in your main application or a test configuration to ensure the scheduler runs.
@Test
void whenSchedulerRuns_thenScheduledTaskIsCalled() throws Exception {
// --- 1. Give the scheduler time to run its task ---
// This is necessary for asynchronous scheduling tests.
TimeUnit.SECONDS.sleep(3);
// --- 2. Verify the spied method was called (the fix confirmation) ---
// If @SpyBean is working correctly with JUnit 5, this verification passes.
// It verifies that the real method was executed at least once.
verify(scheduledService).executeScheduledTask();
}
}
Comment From: philwebb
@SpyBean was deprecated in 3.4 and finally removed in 4.0. The javadoc provides the replacement (org.springframework.test.context.bean.override.mockito.MockitoSpyBean) which is now part of Spring Framework.