Skip to content

Commit aac8850

Browse files
committed
Reinject mocks when context is dirtied before each method
Closes spring-projectsgh-11903
1 parent 61cba64 commit aac8850

File tree

4 files changed

+231
-16
lines changed

4 files changed

+231
-16
lines changed

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

Lines changed: 81 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2016 the original author or authors.
2+
* Copyright 2012-2018 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.
@@ -24,10 +24,10 @@
2424
import org.mockito.Captor;
2525
import org.mockito.MockitoAnnotations;
2626

27-
import org.springframework.context.ApplicationContext;
2827
import org.springframework.test.context.TestContext;
2928
import org.springframework.test.context.TestExecutionListener;
3029
import org.springframework.test.context.support.AbstractTestExecutionListener;
30+
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
3131
import org.springframework.util.ReflectionUtils;
3232
import org.springframework.util.ReflectionUtils.FieldCallback;
3333

@@ -37,16 +37,35 @@
3737
* annotations.
3838
*
3939
* @author Phillip Webb
40+
* @author Andy Wilkinson
4041
* @since 1.4.2
4142
*/
4243
public class MockitoTestExecutionListener extends AbstractTestExecutionListener {
4344

4445
@Override
4546
public void prepareTestInstance(TestContext testContext) throws Exception {
47+
initMocks(testContext);
48+
injectFields(testContext);
49+
}
50+
51+
@Override
52+
public void beforeTestMethod(TestContext testContext) throws Exception {
53+
if (Boolean.TRUE.equals(testContext.getAttribute(
54+
DependencyInjectionTestExecutionListener.REINJECT_DEPENDENCIES_ATTRIBUTE))) {
55+
initMocks(testContext);
56+
reinjectFields(testContext);
57+
}
58+
}
59+
60+
@Override
61+
public int getOrder() {
62+
return 1950;
63+
}
64+
65+
private void initMocks(TestContext testContext) {
4666
if (hasMockitoAnnotations(testContext)) {
4767
MockitoAnnotations.initMocks(testContext.getTestInstance());
4868
}
49-
injectFields(testContext);
5069
}
5170

5271
private boolean hasMockitoAnnotations(TestContext testContext) {
@@ -56,21 +75,46 @@ private boolean hasMockitoAnnotations(TestContext testContext) {
5675
}
5776

5877
private void injectFields(TestContext testContext) {
78+
postProcessFields(testContext, new MockitoFieldHandler() {
79+
80+
@Override
81+
public void handle(MockitoField mockitoField,
82+
MockitoPostProcessor postProcessor) {
83+
postProcessor.inject(mockitoField.field, mockitoField.target,
84+
mockitoField.definition);
85+
}
86+
87+
});
88+
}
89+
90+
private void reinjectFields(final TestContext testContext) {
91+
postProcessFields(testContext, new MockitoFieldHandler() {
92+
93+
@Override
94+
public void handle(MockitoField mockitoField,
95+
MockitoPostProcessor postProcessor) {
96+
ReflectionUtils.makeAccessible(mockitoField.field);
97+
ReflectionUtils.setField(mockitoField.field,
98+
testContext.getTestInstance(), null);
99+
postProcessor.inject(mockitoField.field, mockitoField.target,
100+
mockitoField.definition);
101+
}
102+
103+
});
104+
}
105+
106+
private void postProcessFields(TestContext testContext, MockitoFieldHandler handler) {
59107
DefinitionsParser parser = new DefinitionsParser();
60108
parser.parse(testContext.getTestClass());
61109
if (!parser.getDefinitions().isEmpty()) {
62-
injectFields(testContext, parser);
63-
}
64-
}
65-
66-
private void injectFields(TestContext testContext, DefinitionsParser parser) {
67-
ApplicationContext applicationContext = testContext.getApplicationContext();
68-
MockitoPostProcessor postProcessor = applicationContext
69-
.getBean(MockitoPostProcessor.class);
70-
for (Definition definition : parser.getDefinitions()) {
71-
Field field = parser.getField(definition);
72-
if (field != null) {
73-
postProcessor.inject(field, testContext.getTestInstance(), definition);
110+
MockitoPostProcessor postProcessor = testContext.getApplicationContext()
111+
.getBean(MockitoPostProcessor.class);
112+
for (Definition definition : parser.getDefinitions()) {
113+
Field field = parser.getField(definition);
114+
if (field != null) {
115+
handler.handle(new MockitoField(field, testContext.getTestInstance(),
116+
definition), postProcessor);
117+
}
74118
}
75119
}
76120
}
@@ -98,4 +142,26 @@ public boolean hasAnnotations() {
98142

99143
}
100144

145+
private static final class MockitoField {
146+
147+
private final Field field;
148+
149+
private final Object target;
150+
151+
private final Definition definition;
152+
153+
private MockitoField(Field field, Object instance, Definition definition) {
154+
this.field = field;
155+
this.target = instance;
156+
this.definition = definition;
157+
}
158+
159+
}
160+
161+
private interface MockitoFieldHandler {
162+
163+
void handle(MockitoField mockitoField, MockitoPostProcessor postProcessor);
164+
165+
}
166+
101167
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright 2012-2018 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+
* http://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.Test;
20+
import org.junit.runner.RunWith;
21+
22+
import org.springframework.beans.factory.annotation.Autowired;
23+
import org.springframework.boot.test.mock.mockito.example.ExampleService;
24+
import org.springframework.boot.test.mock.mockito.example.ExampleServiceCaller;
25+
import org.springframework.context.annotation.Configuration;
26+
import org.springframework.context.annotation.Import;
27+
import org.springframework.test.annotation.DirtiesContext;
28+
import org.springframework.test.annotation.DirtiesContext.ClassMode;
29+
import org.springframework.test.annotation.DirtiesContext.MethodMode;
30+
import org.springframework.test.context.junit4.SpringRunner;
31+
32+
import static org.assertj.core.api.Assertions.assertThat;
33+
import static org.mockito.BDDMockito.given;
34+
35+
/**
36+
* Integration tests for using {@link MockBean} with {@link DirtiesContext} and
37+
* {@link MethodMode#BEFORE_METHOD}.
38+
*
39+
* @author Andy Wilkinson
40+
*/
41+
@RunWith(SpringRunner.class)
42+
@DirtiesContext(classMode = ClassMode.BEFORE_EACH_TEST_METHOD)
43+
public class MockBeanWithDirtiesContextClassModeBeforeMethodIntegrationTests {
44+
45+
@MockBean
46+
private ExampleService exampleService;
47+
48+
@Autowired
49+
private ExampleServiceCaller caller;
50+
51+
@Test
52+
public void testMocking() throws Exception {
53+
given(this.exampleService.greeting()).willReturn("Boot");
54+
assertThat(this.caller.sayGreeting()).isEqualTo("I say Boot");
55+
}
56+
57+
@Configuration
58+
@Import(ExampleServiceCaller.class)
59+
static class Config {
60+
61+
}
62+
63+
}

spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockitoTestExecutionListenerTests.java

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2016 the original author or authors.
2+
* Copyright 2012-2018 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.
@@ -28,13 +28,15 @@
2828

2929
import org.springframework.context.ApplicationContext;
3030
import org.springframework.test.context.TestContext;
31+
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
3132

3233
import static org.assertj.core.api.Assertions.assertThat;
3334
import static org.mockito.BDDMockito.given;
3435
import static org.mockito.Matchers.any;
3536
import static org.mockito.Matchers.eq;
3637
import static org.mockito.Mockito.mock;
3738
import static org.mockito.Mockito.verify;
39+
import static org.mockito.Mockito.verifyNoMoreInteractions;
3840

3941
/**
4042
* Tests for {@link MockitoTestExecutionListener}.
@@ -78,6 +80,28 @@ public void prepareTestInstanceShouldInjectMockBean() throws Exception {
7880
assertThat(this.fieldCaptor.getValue().getName()).isEqualTo("mockBean");
7981
}
8082

83+
@Test
84+
public void beforeTestMethodShouldDoNothingWhenDirtiesContextAttributeIsNotSet()
85+
throws Exception {
86+
WithMockBean instance = new WithMockBean();
87+
this.listener.beforeTestMethod(mockTestContext(instance));
88+
verifyNoMoreInteractions(this.postProcessor);
89+
}
90+
91+
@Test
92+
public void beforeTestMethodShouldInjectMockBeanWhenDirtiesContextAttributeIsSet()
93+
throws Exception {
94+
WithMockBean instance = new WithMockBean();
95+
TestContext mockTestContext = mockTestContext(instance);
96+
given(mockTestContext.getAttribute(
97+
DependencyInjectionTestExecutionListener.REINJECT_DEPENDENCIES_ATTRIBUTE))
98+
.willReturn(Boolean.TRUE);
99+
this.listener.beforeTestMethod(mockTestContext);
100+
verify(this.postProcessor).inject(this.fieldCaptor.capture(), eq(instance),
101+
(MockDefinition) any());
102+
assertThat(this.fieldCaptor.getValue().getName()).isEqualTo("mockBean");
103+
}
104+
81105
@SuppressWarnings({ "unchecked", "rawtypes" })
82106
private TestContext mockTestContext(Object instance) {
83107
TestContext testContext = mock(TestContext.class);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright 2012-2018 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+
* http://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.Test;
20+
import org.junit.runner.RunWith;
21+
22+
import org.springframework.beans.factory.annotation.Autowired;
23+
import org.springframework.boot.test.mock.mockito.example.ExampleServiceCaller;
24+
import org.springframework.boot.test.mock.mockito.example.SimpleExampleService;
25+
import org.springframework.context.annotation.Configuration;
26+
import org.springframework.context.annotation.Import;
27+
import org.springframework.test.annotation.DirtiesContext;
28+
import org.springframework.test.annotation.DirtiesContext.ClassMode;
29+
import org.springframework.test.annotation.DirtiesContext.MethodMode;
30+
import org.springframework.test.context.junit4.SpringRunner;
31+
32+
import static org.mockito.Mockito.verify;
33+
34+
/**
35+
* Integration tests for using {@link SpyBean} with {@link DirtiesContext} and
36+
* {@link MethodMode#BEFORE_METHOD}.
37+
*
38+
* @author Andy Wilkinson
39+
*/
40+
@RunWith(SpringRunner.class)
41+
@DirtiesContext(classMode = ClassMode.BEFORE_EACH_TEST_METHOD)
42+
public class SpyBeanWithDirtiesContextClassModeBeforeMethodIntegrationTests {
43+
44+
@SpyBean
45+
private SimpleExampleService exampleService;
46+
47+
@Autowired
48+
private ExampleServiceCaller caller;
49+
50+
@Test
51+
public void testSpying() throws Exception {
52+
this.caller.sayGreeting();
53+
verify(this.exampleService).greeting();
54+
}
55+
56+
@Configuration
57+
@Import(ExampleServiceCaller.class)
58+
static class Config {
59+
60+
}
61+
62+
}

0 commit comments

Comments
 (0)