-
Notifications
You must be signed in to change notification settings - Fork 38.6k
Closed as not planned
Labels
in: testIssues in the test moduleIssues in the test modulestatus: supersededAn issue that has been superseded by anotherAn issue that has been superseded by another
Description
Overview
When an existing bean of the required type is not discovered in the bean factory, Spring Boot's @SpyBean
creates a new instance to spy on by instantiating the required type using its default constructor.
Whereas, @MockitoSpyBean
requires that a bean of the required type exists and throws an IllegalStateException
if an appropriate bean cannot be found.
Consequently, @MockitoSpyBean
cannot be used to spy on a bean that does not exist.
Original Description
@MockitoSpyBean
cannot spy concrete runtime bean type, but Spring Boot's @SpyBean
can.
Example
plugins {
id 'java'
id 'org.springframework.boot' version '3.4.0'
id 'io.spring.dependency-management' version 'latest.release'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
tasks.named('test') {
useJUnitPlatform()
}
package com.example;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
@ContextConfiguration
@SpringJUnitConfig
public class SpyBeanTests {
@MockitoSpyBean
// @SpyBean works
// @Autowired works
SubTestService testService;
@Test
void test() {
assertThat(testService.echo("test")).isEqualTo("test");
}
@Configuration
static class Config {
@Bean
TestService testService() {
return new SubTestService();
}
}
static class SubTestService extends TestService {
}
static class TestService {
String echo(String str) {
return str;
}
}
}
Test fails with:
Caused by: java.lang.IllegalStateException: Unable to select a bean to override by wrapping: found 0 bean instances of type com.example.SpyBeanTests$SubTestService (as required by annotated field 'SpyBeanTests.testService')
at org.springframework.test.context.bean.override.BeanOverrideBeanFactoryPostProcessor.wrapBean(BeanOverrideBeanFactoryPostProcessor.java:242)
at org.springframework.test.context.bean.override.BeanOverrideBeanFactoryPostProcessor.registerBeanOverride(BeanOverrideBeanFactoryPostProcessor.java:113)
at org.springframework.test.context.bean.override.BeanOverrideBeanFactoryPostProcessor.postProcessBeanFactory(BeanOverrideBeanFactoryPostProcessor.java:98)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:363)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:197)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:791)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:609)
at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:221)
at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:110)
at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.loadContext(AbstractDelegatingSmartContextLoader.java:212)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:225)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:152)
... 19 more
The workaround is changing the @Bean
method to return the most specific type, SubTestService
in this example.
Metadata
Metadata
Assignees
Labels
in: testIssues in the test moduleIssues in the test modulestatus: supersededAn issue that has been superseded by anotherAn issue that has been superseded by another