Remove support for Mockito annotations and MockitoSession

This change remove the support for Mockito annotations, `MockitoSession`
and opening/closing of mocks that was inherited from Boot's `@MockBean`
support, as well as the switch to `MockitoSession` made in 1c893e6.

Attempting to take responsability for things Mockito's own JUnit
Jupiter extension does better is not ideal, and we found it leads to
several corner cases which make `SpringExtension` and `MockitoExtension`
incompatible in the current approach.

Instead, this change refocuses our Mockito bean overriding support
exclusively on aspects specific to the Framework. `MockitoExtension`
will thus be usable in conjunction with `SpringExtension` if one needs
to use `@Captor`/`@InitMocks`/`@Mock`/`@Spy` or other Mockito utilities.

See gh-33318
Closes gh-33692
This commit is contained in:
Simon Baslé
2024-10-15 18:29:36 +02:00
parent c261ca3102
commit 6f79c7e70f
14 changed files with 5 additions and 432 deletions

View File

@@ -27,7 +27,6 @@ import org.springframework.core.annotation.AliasFor;
import org.springframework.core.annotation.AnnotationConfigurationException;
import org.springframework.test.context.bean.override.BeanOverrideTestExecutionListener;
import org.springframework.test.context.bean.override.mockito.MockitoResetTestExecutionListener;
import org.springframework.test.context.bean.override.mockito.MockitoTestExecutionListener;
import org.springframework.test.context.event.ApplicationEventsTestExecutionListener;
import org.springframework.test.context.event.EventPublishingTestExecutionListener;
import org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener;
@@ -69,7 +68,6 @@ class TestExecutionListenersTests {
List<Class<?>> expected = asList(ServletTestExecutionListener.class,//
DirtiesContextBeforeModesTestExecutionListener.class,//
ApplicationEventsTestExecutionListener.class,//
MockitoTestExecutionListener.class,//
DependencyInjectionTestExecutionListener.class,//
micrometerListenerClass,//
DirtiesContextTestExecutionListener.class,//
@@ -92,7 +90,6 @@ class TestExecutionListenersTests {
ServletTestExecutionListener.class,//
DirtiesContextBeforeModesTestExecutionListener.class,//
ApplicationEventsTestExecutionListener.class,//
MockitoTestExecutionListener.class,//
DependencyInjectionTestExecutionListener.class,//
micrometerListenerClass,//
DirtiesContextTestExecutionListener.class,//
@@ -114,7 +111,6 @@ class TestExecutionListenersTests {
List<Class<?>> expected = asList(ServletTestExecutionListener.class,//
DirtiesContextBeforeModesTestExecutionListener.class,//
ApplicationEventsTestExecutionListener.class,//
MockitoTestExecutionListener.class,//
DependencyInjectionTestExecutionListener.class,//
micrometerListenerClass,//
DirtiesContextTestExecutionListener.class,//
@@ -137,7 +133,6 @@ class TestExecutionListenersTests {
List<Class<?>> expected = asList(ServletTestExecutionListener.class,//
DirtiesContextBeforeModesTestExecutionListener.class,//
ApplicationEventsTestExecutionListener.class,//
MockitoTestExecutionListener.class,//
DependencyInjectionTestExecutionListener.class,//
BarTestExecutionListener.class,//
micrometerListenerClass,//

View File

@@ -30,9 +30,8 @@ import static org.mockito.BDDMockito.then;
import static org.mockito.Mockito.times;
/**
* Verifies proper handling of the {@link org.mockito.MockitoSession MockitoSession}
* when a {@link MockitoBean @MockitoBean} field is declared in the enclosing class of
* a {@link Nested @Nested} test class.
* Verifies proper reset of mocks when a {@link MockitoBean @MockitoBean} field
* is declared in the enclosing class of a {@link Nested @Nested} test class.
*
* @author Andy Wilkinson
* @author Sam Brannen

View File

@@ -1,63 +0,0 @@
/*
* 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");
}
}
}

View File

@@ -1,56 +0,0 @@
/*
* 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.Test;
import org.mockito.quality.Strictness;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.BDDMockito.when;
import static org.mockito.Mockito.mock;
/**
* Integration tests for explicitly-defined {@link MockitoBeanSettings} with
* lenient stubbing.
*
* @author Simon Baslé
* @since 6.2
*/
@SpringJUnitConfig
@DirtiesContext
@MockitoBeanSettings(Strictness.LENIENT)
class MockitoBeanSettingsLenientIntegrationTests {
@Test
@SuppressWarnings("rawtypes")
void unusedStubbingNotReported() {
List list = mock();
when(list.get(anyInt())).thenReturn(new Object());
}
@Configuration(proxyBeanMethods = false)
static class Config {
// no beans
}
}

View File

@@ -1,141 +0,0 @@
/*
* 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.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.FieldSource;
import org.junit.platform.testkit.engine.EngineTestKit;
import org.junit.platform.testkit.engine.Events;
import org.mockito.exceptions.misusing.UnnecessaryStubbingException;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import static org.junit.jupiter.params.provider.Arguments.argumentSet;
import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;
import static org.junit.platform.testkit.engine.EventConditions.event;
import static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;
import static org.junit.platform.testkit.engine.EventConditions.test;
import static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf;
import static org.junit.platform.testkit.engine.TestExecutionResultConditions.message;
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<?> testCase, int startedCount, int failureCount) {
Events events = EngineTestKit.engine("junit-jupiter")
.selectors(selectClass(testCase))
.execute()
.testEvents()
.assertStatistics(stats -> stats.started(startedCount).failed(failureCount));
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, 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 {
@Test
@SuppressWarnings("rawtypes")
void unnecessaryStub() {
List list = mock();
when(list.get(anyInt())).thenReturn(new Object());
}
}
@SpringJUnitConfig(Config.class)
@DirtiesContext
@MockitoBeanSettings(STRICT_STUBS)
static class ExplicitStrictness extends BaseCase {
}
@SpringJUnitConfig(Config.class)
@DirtiesContext
static class ImplicitStrictnessWithMockitoBean extends BaseCase {
@MockitoBean
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)
static class Config {
// no beans
}
}