Environment: Spring 6.2.0-M1, Java 21 Example project: https://github.com/hantsy/spring6-sandbox/tree/master/test-bean
The CustomerService
is a simple interface, and provides a default impl DefaultCusomterService
, and declares the bean in the Config
.
public interface CustomerService {
Customer findByEmail(String email);
List<Customer> findAll();
}
public class DefaultCustomerService implements CustomerService {
@Override
public Customer findByEmail(String email) {
return new Customer("foo", "bar", "foobar@example.com");
}
@Override
public List<Customer> findAll() {
return List.of(
new Customer("foo", "bar", "foobar@example.com"),
new Customer("foo2", "bar2", "foobar2@example.com")
);
}
}
@Configuration
public class Config {
@Bean
public CustomerService customerService() {
return new DefaultCustomerService();
}
}
I try to write a test like this:
@SpringJUnitConfig(classes = Config.class)
class CustomerServiceTest {
@Autowired
CustomerService realCustomerService;
@TestBean(/*methodName = ""*/) // use methodName to specify a custom factory method.
CustomerService customerService;
// by default method name is {beanName}TestOverride
static CustomerService customerServiceTestOverride() {
return new DummyCustomerService();
}
@Test
public void test(ApplicationContext context) {
assertThat(context.getBean("customerService"))
.isSameAs(this.customerService)
.isInstanceOf(DummyCustomerService.class);
}
@Test
public void testCustomerService() {
var customer = realCustomerService.findByEmail("foobar@example.com");
assertThat(customer.firstName()).isEqualTo("foo");
assertThat(customer.lastName()).isEqualTo("bar");
assertThat(realCustomerService.findAll().size()).isEqualTo(2);
// test bean
var testCustomer = customerService.findByEmail("dummy@example.com");
assertThat(testCustomer.firstName()).isEqualTo("dummy first");
assertThat(testCustomer.lastName()).isEqualTo("dummy last");
assertThat(customerService.findAll().size()).isEqualTo(0);
}
}
I want to inject a real bean in the test, but it is also replaced by the test bean here.
Comment From: sbrannen
I want to inject a real bean in the test, but it is also replaced by the test bean here.
That's to be expected.
As stated in the Javadoc, @TestBean
is used ...
to override a bean instance in the
BeanFactory
.
Furthermore:
it is also replaced in the
BeanFactory
so that other injection points for that bean use the overridden bean instance.
In light of that, I am closing this issue.
Comment From: hantsy
@sbrannen Please expose the strategy
attribute in @TestBean
to allow developer decide to replace or create a new instance for the annotated property.
Comment From: sbrannen
@hantsy, the reason we are introducing @TestBean
is to provide a safe mechanism for reliably overriding an existing bean in the BeanFactory
while simultaneously allowing the overridden bean instance to be injected into injection points that would otherwise have received the original bean.
If you want to add another bean (instead of overriding an existing one), that's already possible via existing features.
Please expose the
strategy
attribute in@TestBean
to allow developer decide to replace or create a new instance for the annotated property.
What would be the benefit of that?
In other words, what concrete use case would that support which does not conflict with the aforementioned goals of @TestBean
?
Comment From: hantsy
What would be the benefit of that?
In other words, what concrete use case would that support which does not conflict with the aforementioned goals of @TestBean?
As described in my original sample codes, I would like let developers decide to create new or replace the existing beans, sometimes we need all cases(mock bean, and real bean) in the same tests.