Skip to content

Commit

Permalink
Ensure that @⁠MockitoBeanSettings is inherited in @⁠Nested test class
Browse files Browse the repository at this point in the history
Closes gh-33685
  • Loading branch information
sbrannen committed Oct 11, 2024
1 parent 7ea0ac5 commit b3cc9a2
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;

import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.TestContextAnnotationUtils;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;

/**
Expand Down Expand Up @@ -80,7 +80,8 @@ public void afterTestMethod(TestContext testContext) {
private static void initMocks(TestContext testContext) {
Class<?> testClass = testContext.getTestClass();
Object testInstance = testContext.getTestInstance();
MockitoBeanSettings annotation = AnnotationUtils.findAnnotation(testClass, MockitoBeanSettings.class);
MockitoBeanSettings annotation =
TestContextAnnotationUtils.findMergedAnnotation(testClass, MockitoBeanSettings.class);
Strictness strictness = (annotation != null ? annotation.value() : Strictness.STRICT_STUBS);
testContext.setAttribute(MOCKITO_SESSION_ATTRIBUTE_NAME, initMockitoSession(testInstance, strictness));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright 2002-2024 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.test.context.bean.override.mockito;

import java.util.List;

import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.quality.Strictness;

import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit.jupiter.SpringExtension;

import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.BDDMockito.when;
import static org.mockito.Mockito.mock;

/**
* Tests which verify that strictness configured via
* {@link MockitoBeanSettings @MockitoBeanSettings} is inherited in
* {@link Nested @Nested} test classes.
*
* @author Sam Brannen
* @since 6.2
*/
@ExtendWith(SpringExtension.class)
@TestExecutionListeners(MockitoTestExecutionListener.class)
@MockitoBeanSettings(Strictness.LENIENT)
class MockitoBeanSettingsInheritedStrictnessTests {

// Should inherit Strictness.LENIENT.
@Nested
class NestedTests {

@Test
@SuppressWarnings("rawtypes")
void unnecessaryStub() {
List list = mock();
when(list.get(anyInt())).thenReturn("enigma");

// We intentionally do NOT perform any assertions against the mock,
// because we want to ensure that an UnnecessaryStubbingException is
// not thrown by Mockito.
// assertThat(list.get(1)).isEqualTo("enigma");
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@

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

import java.time.format.DateTimeFormatter;
import java.util.List;

import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
Expand All @@ -41,36 +41,43 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.BDDMockito.when;
import static org.mockito.Mockito.mock;
import static org.mockito.quality.Strictness.LENIENT;
import static org.mockito.quality.Strictness.STRICT_STUBS;

/**
* Integration tests ensuring unnecessary stubbing is reported in various
* cases where a strict style is chosen or assumed.
*
* @author Simon Baslé
* @author Sam Brannen
* @since 6.2
*/
class MockitoBeanSettingsStrictIntegrationTests {

@ParameterizedTest
@FieldSource("strictCases")
void unusedStubbingIsReported(Class<?> forCase) {
void unusedStubbingIsReported(Class<?> testCase, int startedCount, int failureCount) {
Events events = EngineTestKit.engine("junit-jupiter")
.selectors(selectClass(forCase))
.selectors(selectClass(testCase))
.execute()
.testEvents()
.assertStatistics(stats -> stats.started(1).failed(1));
.assertStatistics(stats -> stats.started(startedCount).failed(failureCount));

events.assertThatEvents().haveExactly(1,
events.assertThatEvents().haveExactly(failureCount,
event(test("unnecessaryStub"),
finishedWithFailure(
instanceOf(UnnecessaryStubbingException.class),
message(msg -> msg.contains("Unnecessary stubbings detected.")))));
}

static final List<Arguments> strictCases = List.of(
argumentSet("explicit strictness", ExplicitStrictness.class),
argumentSet("implicit strictness with @MockitoBean on field", ImplicitStrictnessWithMockitoBean.class)
argumentSet("explicit strictness", ExplicitStrictness.class, 1, 1),
argumentSet("explicit strictness on enclosing class", ExplicitStrictnessEnclosingTestCase.class, 1, 1),
argumentSet("implicit strictness with @MockitoBean on field", ImplicitStrictnessWithMockitoBean.class, 1, 1),
// 3, 1 --> The tests in LenientStubbingNestedTestCase and InheritedLenientStubbingNestedTestCase
// should not result in an UnnecessaryStubbingException.
argumentSet("implicit strictness overridden and inherited in @Nested test classes",
ImplicitStrictnessWithMockitoBeanEnclosingTestCase.class, 3, 1)
);

abstract static class BaseCase {
Expand All @@ -94,8 +101,36 @@ static class ExplicitStrictness extends BaseCase {
static class ImplicitStrictnessWithMockitoBean extends BaseCase {

@MockitoBean
@SuppressWarnings("unused")
DateTimeFormatter ignoredMock;
Runnable ignoredMock;
}

@SpringJUnitConfig(Config.class)
@DirtiesContext
@MockitoBeanSettings(STRICT_STUBS)
static class ExplicitStrictnessEnclosingTestCase {

@Nested
class NestedTestCase extends BaseCase {
}
}

@SpringJUnitConfig(Config.class)
@DirtiesContext
static class ImplicitStrictnessWithMockitoBeanEnclosingTestCase extends BaseCase {

@MockitoBean
Runnable ignoredMock;

@Nested
// Overrides implicit STRICT_STUBS
@MockitoBeanSettings(LENIENT)
class LenientStubbingNestedTestCase extends BaseCase {

@Nested
// Inherits LENIENT
class InheritedLenientStubbingNestedTestCase extends BaseCase {
}
}
}

@Configuration(proxyBeanMethods = false)
Expand Down

0 comments on commit b3cc9a2

Please sign in to comment.