Skip to content

Commit b3cc9a2

Browse files
committed
Ensure that @⁠MockitoBeanSettings is inherited in @⁠Nested test class
Closes gh-33685
1 parent 7ea0ac5 commit b3cc9a2

File tree

3 files changed

+110
-11
lines changed

3 files changed

+110
-11
lines changed

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

+3-2
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
import org.mockito.MockitoSession;
2121
import org.mockito.quality.Strictness;
2222

23-
import org.springframework.core.annotation.AnnotationUtils;
2423
import org.springframework.test.context.TestContext;
24+
import org.springframework.test.context.TestContextAnnotationUtils;
2525
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
2626

2727
/**
@@ -80,7 +80,8 @@ public void afterTestMethod(TestContext testContext) {
8080
private static void initMocks(TestContext testContext) {
8181
Class<?> testClass = testContext.getTestClass();
8282
Object testInstance = testContext.getTestInstance();
83-
MockitoBeanSettings annotation = AnnotationUtils.findAnnotation(testClass, MockitoBeanSettings.class);
83+
MockitoBeanSettings annotation =
84+
TestContextAnnotationUtils.findMergedAnnotation(testClass, MockitoBeanSettings.class);
8485
Strictness strictness = (annotation != null ? annotation.value() : Strictness.STRICT_STUBS);
8586
testContext.setAttribute(MOCKITO_SESSION_ATTRIBUTE_NAME, initMockitoSession(testInstance, strictness));
8687
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright 2002-2024 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.test.context.bean.override.mockito;
18+
19+
import java.util.List;
20+
21+
import org.junit.jupiter.api.Nested;
22+
import org.junit.jupiter.api.Test;
23+
import org.junit.jupiter.api.extension.ExtendWith;
24+
import org.mockito.quality.Strictness;
25+
26+
import org.springframework.test.context.TestExecutionListeners;
27+
import org.springframework.test.context.junit.jupiter.SpringExtension;
28+
29+
import static org.mockito.ArgumentMatchers.anyInt;
30+
import static org.mockito.BDDMockito.when;
31+
import static org.mockito.Mockito.mock;
32+
33+
/**
34+
* Tests which verify that strictness configured via
35+
* {@link MockitoBeanSettings @MockitoBeanSettings} is inherited in
36+
* {@link Nested @Nested} test classes.
37+
*
38+
* @author Sam Brannen
39+
* @since 6.2
40+
*/
41+
@ExtendWith(SpringExtension.class)
42+
@TestExecutionListeners(MockitoTestExecutionListener.class)
43+
@MockitoBeanSettings(Strictness.LENIENT)
44+
class MockitoBeanSettingsInheritedStrictnessTests {
45+
46+
// Should inherit Strictness.LENIENT.
47+
@Nested
48+
class NestedTests {
49+
50+
@Test
51+
@SuppressWarnings("rawtypes")
52+
void unnecessaryStub() {
53+
List list = mock();
54+
when(list.get(anyInt())).thenReturn("enigma");
55+
56+
// We intentionally do NOT perform any assertions against the mock,
57+
// because we want to ensure that an UnnecessaryStubbingException is
58+
// not thrown by Mockito.
59+
// assertThat(list.get(1)).isEqualTo("enigma");
60+
}
61+
}
62+
63+
}

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

+44-9
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@
1616

1717
package org.springframework.test.context.bean.override.mockito;
1818

19-
import java.time.format.DateTimeFormatter;
2019
import java.util.List;
2120

21+
import org.junit.jupiter.api.Nested;
2222
import org.junit.jupiter.api.Test;
2323
import org.junit.jupiter.params.ParameterizedTest;
2424
import org.junit.jupiter.params.provider.Arguments;
@@ -41,36 +41,43 @@
4141
import static org.mockito.ArgumentMatchers.anyInt;
4242
import static org.mockito.BDDMockito.when;
4343
import static org.mockito.Mockito.mock;
44+
import static org.mockito.quality.Strictness.LENIENT;
4445
import static org.mockito.quality.Strictness.STRICT_STUBS;
4546

4647
/**
4748
* Integration tests ensuring unnecessary stubbing is reported in various
4849
* cases where a strict style is chosen or assumed.
4950
*
5051
* @author Simon Baslé
52+
* @author Sam Brannen
5153
* @since 6.2
5254
*/
5355
class MockitoBeanSettingsStrictIntegrationTests {
5456

5557
@ParameterizedTest
5658
@FieldSource("strictCases")
57-
void unusedStubbingIsReported(Class<?> forCase) {
59+
void unusedStubbingIsReported(Class<?> testCase, int startedCount, int failureCount) {
5860
Events events = EngineTestKit.engine("junit-jupiter")
59-
.selectors(selectClass(forCase))
61+
.selectors(selectClass(testCase))
6062
.execute()
6163
.testEvents()
62-
.assertStatistics(stats -> stats.started(1).failed(1));
64+
.assertStatistics(stats -> stats.started(startedCount).failed(failureCount));
6365

64-
events.assertThatEvents().haveExactly(1,
66+
events.assertThatEvents().haveExactly(failureCount,
6567
event(test("unnecessaryStub"),
6668
finishedWithFailure(
6769
instanceOf(UnnecessaryStubbingException.class),
6870
message(msg -> msg.contains("Unnecessary stubbings detected.")))));
6971
}
7072

7173
static final List<Arguments> strictCases = List.of(
72-
argumentSet("explicit strictness", ExplicitStrictness.class),
73-
argumentSet("implicit strictness with @MockitoBean on field", ImplicitStrictnessWithMockitoBean.class)
74+
argumentSet("explicit strictness", ExplicitStrictness.class, 1, 1),
75+
argumentSet("explicit strictness on enclosing class", ExplicitStrictnessEnclosingTestCase.class, 1, 1),
76+
argumentSet("implicit strictness with @MockitoBean on field", ImplicitStrictnessWithMockitoBean.class, 1, 1),
77+
// 3, 1 --> The tests in LenientStubbingNestedTestCase and InheritedLenientStubbingNestedTestCase
78+
// should not result in an UnnecessaryStubbingException.
79+
argumentSet("implicit strictness overridden and inherited in @Nested test classes",
80+
ImplicitStrictnessWithMockitoBeanEnclosingTestCase.class, 3, 1)
7481
);
7582

7683
abstract static class BaseCase {
@@ -94,8 +101,36 @@ static class ExplicitStrictness extends BaseCase {
94101
static class ImplicitStrictnessWithMockitoBean extends BaseCase {
95102

96103
@MockitoBean
97-
@SuppressWarnings("unused")
98-
DateTimeFormatter ignoredMock;
104+
Runnable ignoredMock;
105+
}
106+
107+
@SpringJUnitConfig(Config.class)
108+
@DirtiesContext
109+
@MockitoBeanSettings(STRICT_STUBS)
110+
static class ExplicitStrictnessEnclosingTestCase {
111+
112+
@Nested
113+
class NestedTestCase extends BaseCase {
114+
}
115+
}
116+
117+
@SpringJUnitConfig(Config.class)
118+
@DirtiesContext
119+
static class ImplicitStrictnessWithMockitoBeanEnclosingTestCase extends BaseCase {
120+
121+
@MockitoBean
122+
Runnable ignoredMock;
123+
124+
@Nested
125+
// Overrides implicit STRICT_STUBS
126+
@MockitoBeanSettings(LENIENT)
127+
class LenientStubbingNestedTestCase extends BaseCase {
128+
129+
@Nested
130+
// Inherits LENIENT
131+
class InheritedLenientStubbingNestedTestCase extends BaseCase {
132+
}
133+
}
99134
}
100135

101136
@Configuration(proxyBeanMethods = false)

0 commit comments

Comments
 (0)