-
Notifications
You must be signed in to change notification settings - Fork 40.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix spying on scoped beans with @SpyBean
Previously, when spying on a scoped bean the creation of the spy would be performed using the scoped proxy. This would result in the spy being unable to spy on any of the target bean's methods as the scoped proxy's implementations of those methods would be final. This commit updates MockitoPostProcessor so that the creation of the spy and injection of the @SpyBean-annotated field is performed using the scoped target. The scoped target has not be proxied so this allows Mockito to spy on all of its methods. Closes gh-17817
- Loading branch information
1 parent
0fb0eb6
commit 52050c1
Showing
2 changed files
with
130 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
113 changes: 113 additions & 0 deletions
113
...ework/boot/test/mock/mockito/SpyBeanOnTestFieldForExistingScopedBeanIntegrationTests.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
/* | ||
* Copyright 2012-2019 the original author or authors. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package org.springframework.boot.test.mock.mockito; | ||
|
||
import org.junit.Test; | ||
import org.junit.runner.RunWith; | ||
|
||
import org.springframework.beans.factory.ObjectFactory; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.beans.factory.config.CustomScopeConfigurer; | ||
import org.springframework.boot.test.mock.mockito.SpyBeanOnTestFieldForExistingScopedBeanIntegrationTests.SpyBeanOnTestFieldForExistingScopedBeanConfig; | ||
import org.springframework.boot.test.mock.mockito.example.ExampleService; | ||
import org.springframework.boot.test.mock.mockito.example.ExampleServiceCaller; | ||
import org.springframework.boot.test.mock.mockito.example.SimpleExampleService; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.context.annotation.Import; | ||
import org.springframework.context.annotation.Scope; | ||
import org.springframework.context.annotation.ScopedProxyMode; | ||
import org.springframework.test.context.ContextConfiguration; | ||
import org.springframework.test.context.junit4.SpringRunner; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.mockito.Mockito.verify; | ||
|
||
/** | ||
* Test {@link SpyBean @SpyBean} on a test class field can be used to replace existing | ||
* scoped beans. | ||
* | ||
* @author Andy Wilkinson | ||
*/ | ||
@RunWith(SpringRunner.class) | ||
@ContextConfiguration(classes = SpyBeanOnTestFieldForExistingScopedBeanConfig.class) | ||
public class SpyBeanOnTestFieldForExistingScopedBeanIntegrationTests { | ||
|
||
@SpyBean | ||
private ExampleService exampleService; | ||
|
||
@Autowired | ||
private ExampleServiceCaller caller; | ||
|
||
@Test | ||
public void testSpying() { | ||
assertThat(this.caller.sayGreeting()).isEqualTo("I say simple"); | ||
verify(this.exampleService).greeting(); | ||
} | ||
|
||
@Configuration | ||
@Import({ ExampleServiceCaller.class }) | ||
static class SpyBeanOnTestFieldForExistingScopedBeanConfig { | ||
|
||
@Bean | ||
@Scope(scopeName = "custom", proxyMode = ScopedProxyMode.TARGET_CLASS) | ||
SimpleExampleService simpleExampleService() { | ||
return new SimpleExampleService(); | ||
} | ||
|
||
@Bean | ||
static CustomScopeConfigurer customScopeConfigurer() { | ||
CustomScopeConfigurer configurer = new CustomScopeConfigurer(); | ||
configurer.addScope("custom", new org.springframework.beans.factory.config.Scope() { | ||
|
||
private Object bean; | ||
|
||
@Override | ||
public Object resolveContextualObject(String key) { | ||
throw new UnsupportedOperationException(); | ||
} | ||
|
||
@Override | ||
public Object remove(String name) { | ||
throw new UnsupportedOperationException(); | ||
} | ||
|
||
@Override | ||
public void registerDestructionCallback(String name, Runnable callback) { | ||
throw new UnsupportedOperationException(); | ||
} | ||
|
||
@Override | ||
public String getConversationId() { | ||
throw new UnsupportedOperationException(); | ||
} | ||
|
||
@Override | ||
public Object get(String name, ObjectFactory<?> objectFactory) { | ||
if (this.bean == null) { | ||
this.bean = objectFactory.getObject(); | ||
} | ||
return this.bean; | ||
} | ||
|
||
}); | ||
return configurer; | ||
} | ||
|
||
} | ||
|
||
} |