Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c0c94d5

Browse files
committed
Reject attempt to use @⁠MockitoSpyBean with a scoped proxy
Prior to this commit, an attempt to use @⁠MockitoSpyBean to spy on a scoped proxy configured with @⁠Scope(proxyMode = ScopedProxyMode.TARGET_CLASS) resulted in an exception thrown by Mockito when the spy was stubbed. The exception message stated "Failed to unwrap proxied object" but did not provide any further insight or context for the user. The reason is that ScopedProxyFactoryBean is used to create such a scoped proxy, which uses a SimpleBeanTargetSource, which is not a static TargetSource. Consequently, SpringMockResolver.getUltimateTargetObject(Object) is not able to unwrap the proxy. In order to improve diagnostics for users, this commit eagerly detects an attempt to spy on a scoped proxy and throws an exception with a meaningful message. The following is an example, trimmed stack trace from the test suite. org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'myScopedProxy': Post-processing of FactoryBean's singleton object failed ... Caused by: java.lang.IllegalStateException: @⁠MockitoSpyBean cannot be applied to bean 'myScopedProxy', because it is a Spring AOP proxy with a non-static TargetSource. Perhaps you have attempted to spy on a scoped proxy, which is not supported. at ...MockitoSpyBeanOverrideHandler.createSpy(MockitoSpyBeanOverrideHandler.java:78) Closes gh-35722
1 parent ad22a99 commit c0c94d5

File tree

3 files changed

+90
-0
lines changed

3 files changed

+90
-0
lines changed

spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanOverrideHandler.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,11 @@ protected Object createOverrideInstance(String beanName, @Nullable BeanDefinitio
6969
}
7070

7171
private Object createSpy(String name, Object instance) {
72+
SpringMockResolver.rejectUnsupportedSpyTarget(name, instance);
7273
Class<?> resolvedTypeToOverride = getBeanType().resolve();
7374
Assert.notNull(resolvedTypeToOverride, "Failed to resolve type to override");
7475
Assert.isInstanceOf(resolvedTypeToOverride, instance);
76+
7577
if (Mockito.mockingDetails(instance).isSpy()) {
7678
return instance;
7779
}

spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/SpringMockResolver.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,25 @@ static Object getUltimateTargetObject(Object candidate) {
7979
return candidate;
8080
}
8181

82+
/**
83+
* Reject the supplied bean if it is not a supported candidate to spy on.
84+
* <p>Specifically, this method ensures that the bean is not a Spring AOP proxy
85+
* with a non-static {@link TargetSource}.
86+
* @param beanName the name of the bean to spy on
87+
* @param bean the bean to spy on
88+
* @since 7.0
89+
* @see #getUltimateTargetObject(Object)
90+
*/
91+
static void rejectUnsupportedSpyTarget(String beanName, Object bean) throws IllegalStateException {
92+
if (SPRING_AOP_PRESENT) {
93+
if (AopUtils.isAopProxy(bean) && bean instanceof Advised advised &&
94+
!advised.getTargetSource().isStatic()) {
95+
throw new IllegalStateException("""
96+
@MockitoSpyBean cannot be applied to bean '%s', because it is a Spring AOP proxy \
97+
with a non-static TargetSource. Perhaps you have attempted to spy on a scoped proxy, \
98+
which is not supported.""".formatted(beanName));
99+
}
100+
}
101+
}
102+
82103
}

spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanConfigurationErrorTests.java

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,22 @@
2020

2121
import org.junit.jupiter.api.Test;
2222

23+
import org.springframework.beans.factory.BeanCreationException;
24+
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
25+
import org.springframework.context.annotation.Scope;
26+
import org.springframework.context.annotation.ScopedProxyMode;
2327
import org.springframework.context.support.GenericApplicationContext;
28+
import org.springframework.stereotype.Component;
2429
import org.springframework.test.context.bean.override.BeanOverrideContextCustomizerTestUtils;
2530

31+
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
2632
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
2733

2834
/**
2935
* Tests for {@link MockitoSpyBean @MockitoSpyBean}.
3036
*
3137
* @author Stephane Nicoll
38+
* @author Sam Brannen
3239
*/
3340
class MockitoSpyBeanConfigurationErrorTests {
3441

@@ -73,6 +80,39 @@ void contextCustomizerCannotBeCreatedWithTooManyBeansOfThatType() {
7380
List.of("bean1", "bean2"));
7481
}
7582

83+
@Test // gh-35722
84+
void mockitoSpyBeanCannotSpyOnScopedProxy() {
85+
var context = new AnnotationConfigApplicationContext();
86+
context.register(MyScopedProxy.class);
87+
BeanOverrideContextCustomizerTestUtils.customizeApplicationContext(ScopedProxyTestCase.class, context);
88+
context.refresh();
89+
90+
assertThatExceptionOfType(BeanCreationException.class)
91+
.isThrownBy(() -> context.getBean(MyScopedProxy.class))
92+
.havingRootCause()
93+
.isInstanceOf(IllegalStateException.class)
94+
.withMessage("""
95+
@MockitoSpyBean cannot be applied to bean 'myScopedProxy', because it is a \
96+
Spring AOP proxy with a non-static TargetSource. Perhaps you have attempted \
97+
to spy on a scoped proxy, which is not supported.""");
98+
}
99+
100+
@Test // gh-35722
101+
void mockitoSpyBeanCannotSpyOnSelfInjectionScopedProxy() {
102+
var context = new AnnotationConfigApplicationContext();
103+
context.register(MySelfInjectionScopedProxy.class);
104+
BeanOverrideContextCustomizerTestUtils.customizeApplicationContext(SelfInjectionScopedProxyTestCase.class, context);
105+
106+
assertThatExceptionOfType(BeanCreationException.class)
107+
.isThrownBy(context::refresh)
108+
.havingRootCause()
109+
.isInstanceOf(IllegalStateException.class)
110+
.withMessage("""
111+
@MockitoSpyBean cannot be applied to bean 'mySelfInjectionScopedProxy', because it \
112+
is a Spring AOP proxy with a non-static TargetSource. Perhaps you have attempted \
113+
to spy on a scoped proxy, which is not supported.""");
114+
}
115+
76116

77117
static class ByTypeSingleLookup {
78118

@@ -88,4 +128,31 @@ static class ByNameSingleLookup {
88128

89129
}
90130

131+
static class ScopedProxyTestCase {
132+
133+
@MockitoSpyBean
134+
MyScopedProxy myScopedProxy;
135+
136+
}
137+
138+
static class SelfInjectionScopedProxyTestCase {
139+
140+
@MockitoSpyBean
141+
MySelfInjectionScopedProxy mySelfInjectionScopedProxy;
142+
143+
}
144+
145+
@Component("myScopedProxy")
146+
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
147+
static class MyScopedProxy {
148+
}
149+
150+
@Component("mySelfInjectionScopedProxy")
151+
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
152+
static class MySelfInjectionScopedProxy {
153+
154+
MySelfInjectionScopedProxy(MySelfInjectionScopedProxy self) {
155+
}
156+
}
157+
91158
}

0 commit comments

Comments
 (0)