In integration testing, it is often necessary to replace a real application bean with a simplified or controlled version. Traditionally, this required custom test configurations or context hierarchies.
@TestBean provides a concise mechanism to override or define beans specifically for a test. The replacement is applied at context creation time, ensuring that all dependent beans receive the test-specific instance.
Java source and doc
Definition of TestBeanVersion: 7.0.4 package org.springframework.test.context.bean.override.convention;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@BeanOverride(TestBeanOverrideProcessor.class)
@Reflective(TestBeanReflectiveProcessor.class)
public @interface TestBean {
@AliasFor("name")
String value() default ""; 1
@AliasFor("value")
String name() default ""; 2
String methodName() default ""; 3
String contextName() default ""; 4
boolean enforceOverride() default false; 5
}
Bean Resolution
By default, @TestBean determines the bean to override from the type of the annotated field. If multiple beans match, a @Qualifier can be used to resolve the ambiguity. When no @Qualifier is present, the field name is used as a fallback. Alternatively, a specific bean can be targeted by explicitly setting the value or name attribute.
The static Factory Method
The test bean instance is created using a zero-argument static factory method whose return type matches the annotated field. The factory method may be declared in the same class as the @TestBean field, in any superclass, in implemented interfaces, or in an enclosing class when the field is declared in a nested test class.
Alternatively, a factory method can be defined in an external class and referenced using its fully qualified name with the format <fully-qualified-class-name>#<method-name>, for example: @TestBean(methodName = "org.example.TestUtils#createCustomerRepository").
Spring determines the factory method for @TestBean using the following rules.
- If
methodName is specified, Spring looks for a static method with that exact name.
- Otherwise, Spring searches for a single static method whose name matches either the annotated field name or the explicitly specified bean name.
Example
This example replaces a service bean with a test-specific implementation and verifies that the overridden behavior is used during execution.
Spring App
package com.logicbig.example;
public interface MessageService {
String message();
}
package com.logicbig.example;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
MessageService messageService() {
return () -> "production message";
}
public static void main(String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(AppConfig.class);
MessageService service = context.getBean(MessageService.class);
System.out.println(service.message());
context.close();
}
}
Outputproduction message
Integration Test
package com.logicbig.example;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.bean.override.convention.TestBean;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = AppConfig.class)
public class MyTest {
@TestBean
MessageService messageService;
static MessageService messageService() {
return () -> "test message";
}
@Test
void myTestMethod() {
Assertions.assertEquals("test message", messageService.message());
}
}
Output$ mvn test [INFO] Scanning for projects... [INFO] [INFO] -------------------< com.logicbig.example:test-bean >------------------- [INFO] Building test-bean 1.0-SNAPSHOT [INFO] from pom.xml [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- resources:3.3.1:resources (default-resources) @ test-bean --- [WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory D:\example-projects\spring-core-testing\test-bean\src\main\resources [INFO] [INFO] --- compiler:3.11.0:compile (default-compile) @ test-bean --- [INFO] Nothing to compile - all classes are up to date [INFO] [INFO] --- resources:3.3.1:testResources (default-testResources) @ test-bean --- [WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory D:\example-projects\spring-core-testing\test-bean\src\test\resources [INFO] [INFO] --- compiler:3.11.0:testCompile (default-testCompile) @ test-bean --- [INFO] Changes detected - recompiling the module! :source [WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent! [INFO] Compiling 1 source file with javac [debug target 21] to target\test-classes [INFO] [INFO] --- surefire:3.2.5:test (default-test) @ test-bean --- [INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider [WARNING] file.encoding cannot be set as system property, use <argLine>-Dfile.encoding=...</argLine> instead [INFO] [INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running com.logicbig.example.MyTest [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.683 s -- in com.logicbig.example.MyTest [INFO] [INFO] Results: [INFO] [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 [INFO] [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 3.842 s [INFO] Finished at: 2026-02-09T11:01:38+08:00 [INFO] ------------------------------------------------------------------------
Conclusion
The output confirms that the service bean used by the application context was replaced by the test-provided instance. This demonstrates how @TestBean simplifies bean overriding without additional configuration classes or XML.
Example ProjectDependencies and Technologies Used: - spring-context 7.0.4 (Spring Context)
Version Compatibility: 6.2.0 - 7.0.4 Version compatibilities of spring-context with this example:
- 6.2.0
- 6.2.1
- 6.2.2
- 6.2.3
- 6.2.4
- 6.2.5
- 6.2.6
- 6.2.7
- 6.2.8
- 6.2.9
- 6.2.10
- 6.2.11
- 6.2.12
- 6.2.13
- 6.2.14
- 6.2.15
- 6.2.16
- 7.0.0
- 7.0.1
- 7.0.2
- 7.0.3
- 7.0.4
Versions in green have been tested.
- spring-test 7.0.4 (Spring TestContext Framework)
- junit-jupiter-engine 6.0.2 (Module "junit-jupiter-engine" of JUnit)
- JDK 25
- Maven 3.9.11
|