Skip to content

Commit 95d67a9

Browse files
committed
Merge branch 'gh-29639' into 2.6.x
Closes gh-29909
2 parents f919c99 + 9a3f053 commit 95d67a9

File tree

2 files changed

+96
-2
lines changed

2 files changed

+96
-2
lines changed

spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoPostProcessor.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2021 the original author or authors.
2+
* Copyright 2012-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -26,6 +26,7 @@
2626
import java.util.Map;
2727
import java.util.Set;
2828
import java.util.TreeSet;
29+
import java.util.concurrent.ConcurrentHashMap;
2930

3031
import org.springframework.aop.scope.ScopedProxyUtils;
3132
import org.springframework.beans.BeansException;
@@ -433,6 +434,8 @@ static class SpyPostProcessor implements SmartInstantiationAwareBeanPostProcesso
433434

434435
private static final String BEAN_NAME = SpyPostProcessor.class.getName();
435436

437+
private final Map<String, Object> earlySpyReferences = new ConcurrentHashMap<>(16);
438+
436439
private final MockitoPostProcessor mockitoPostProcessor;
437440

438441
SpyPostProcessor(MockitoPostProcessor mockitoPostProcessor) {
@@ -446,6 +449,10 @@ public int getOrder() {
446449

447450
@Override
448451
public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
452+
if (bean instanceof FactoryBean) {
453+
return bean;
454+
}
455+
this.earlySpyReferences.put(getCacheKey(bean, beanName), bean);
449456
return this.mockitoPostProcessor.createSpyIfNecessary(bean, beanName);
450457
}
451458

@@ -454,7 +461,14 @@ public Object postProcessAfterInitialization(Object bean, String beanName) throw
454461
if (bean instanceof FactoryBean) {
455462
return bean;
456463
}
457-
return this.mockitoPostProcessor.createSpyIfNecessary(bean, beanName);
464+
if (this.earlySpyReferences.remove(getCacheKey(bean, beanName)) != bean) {
465+
return this.mockitoPostProcessor.createSpyIfNecessary(bean, beanName);
466+
}
467+
return bean;
468+
}
469+
470+
private String getCacheKey(Object bean, String beanName) {
471+
return StringUtils.hasLength(beanName) ? beanName : bean.getClass().getName();
458472
}
459473

460474
static void register(BeanDefinitionRegistry registry) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
* Copyright 2012-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.test.mock.mockito;
18+
19+
import org.junit.jupiter.api.Test;
20+
import org.junit.jupiter.api.extension.ExtendWith;
21+
22+
import org.springframework.beans.factory.annotation.Autowired;
23+
import org.springframework.boot.test.mock.mockito.SpyBeanOnTestFieldForExistingCircularBeansIntegrationTests.SpyBeanOnTestFieldForExistingCircularBeansConfig;
24+
import org.springframework.context.annotation.Import;
25+
import org.springframework.test.context.ContextConfiguration;
26+
import org.springframework.test.context.junit.jupiter.SpringExtension;
27+
28+
import static org.mockito.BDDMockito.then;
29+
30+
/**
31+
* Test {@link SpyBean @SpyBean} on a test class field can be used to replace existing
32+
* beans with circular dependencies.
33+
*
34+
* @author Andy Wilkinson
35+
*/
36+
@ExtendWith(SpringExtension.class)
37+
@ContextConfiguration(classes = SpyBeanOnTestFieldForExistingCircularBeansConfig.class)
38+
class SpyBeanOnTestFieldForExistingCircularBeansIntegrationTests {
39+
40+
@SpyBean
41+
private One one;
42+
43+
@Autowired
44+
private Two two;
45+
46+
@Test
47+
void beanWithCircularDependenciesCanBeSpied() {
48+
this.two.callOne();
49+
then(this.one).should().someMethod();
50+
}
51+
52+
@Import({ One.class, Two.class })
53+
static class SpyBeanOnTestFieldForExistingCircularBeansConfig {
54+
55+
}
56+
57+
static class One {
58+
59+
@Autowired
60+
@SuppressWarnings("unused")
61+
private Two two;
62+
63+
void someMethod() {
64+
65+
}
66+
67+
}
68+
69+
static class Two {
70+
71+
@Autowired
72+
private One one;
73+
74+
void callOne() {
75+
this.one.someMethod();
76+
}
77+
78+
}
79+
80+
}

0 commit comments

Comments
 (0)