Honor MockReset strategy for @MockitoBean and @MockitoSpyBean
Commit 6c2cba5d8a
introduced a regression by inadvertently removing the
MockReset strategy comparison when resetting @MockitoBean and
@MockitoSpyBean mocks.
This commit reinstates the MockReset strategy check and introduces
tests for this feature.
Closes gh-33941
This commit is contained in:
parent
2b840ee7ef
commit
0088b9c7f8
|
@ -99,9 +99,10 @@ public enum MockReset {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the {@link MockReset} associated with the given mock.
|
* Get the {@link MockReset} strategy associated with the given mock.
|
||||||
* @param mock the source mock
|
* @param mock the mock
|
||||||
* @return the reset type (never {@code null})
|
* @return the reset strategy for the given mock, or {@link MockReset#NONE}
|
||||||
|
* if no strategy is associated with the given mock
|
||||||
*/
|
*/
|
||||||
static MockReset get(Object mock) {
|
static MockReset get(Object mock) {
|
||||||
MockingDetails mockingDetails = Mockito.mockingDetails(mock);
|
MockingDetails mockingDetails = Mockito.mockingDetails(mock);
|
||||||
|
|
|
@ -37,8 +37,18 @@ class MockitoBeans {
|
||||||
this.beans.add(bean);
|
this.beans.add(bean);
|
||||||
}
|
}
|
||||||
|
|
||||||
void resetAll() {
|
/**
|
||||||
this.beans.forEach(Mockito::reset);
|
* Reset all Mockito beans configured with the supplied {@link MockReset} strategy.
|
||||||
|
* <p>No mocks will be reset if the supplied strategy is {@link MockReset#NONE}.
|
||||||
|
*/
|
||||||
|
void resetAll(MockReset reset) {
|
||||||
|
if (reset != MockReset.NONE) {
|
||||||
|
for (Object bean : this.beans) {
|
||||||
|
if (reset == MockReset.get(bean)) {
|
||||||
|
Mockito.reset(bean);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,7 +116,7 @@ public class MockitoResetTestExecutionListener extends AbstractTestExecutionList
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
beanFactory.getBean(MockitoBeans.class).resetAll();
|
beanFactory.getBean(MockitoBeans.class).resetAll(reset);
|
||||||
}
|
}
|
||||||
catch (NoSuchBeanDefinitionException ex) {
|
catch (NoSuchBeanDefinitionException ex) {
|
||||||
// Continue
|
// Continue
|
||||||
|
|
|
@ -0,0 +1,133 @@
|
||||||
|
/*
|
||||||
|
* 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 org.junit.jupiter.api.AfterAll;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.MethodOrderer;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.TestInfo;
|
||||||
|
import org.junit.jupiter.api.TestMethodOrder;
|
||||||
|
import org.junit.jupiter.api.extension.AfterEachCallback;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||||
|
|
||||||
|
import org.springframework.test.context.bean.override.mockito.MockResetStrategiesIntegrationTests.MockVerificationExtension;
|
||||||
|
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.mockito.BDDMockito.given;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integration tests for {@link MockitoBean @MockitoBean} fields with different
|
||||||
|
* {@link MockReset} strategies.
|
||||||
|
*
|
||||||
|
* @author Sam Brannen
|
||||||
|
* @since 6.2.1
|
||||||
|
* @see MockitoResetTestExecutionListenerWithoutMockitoAnnotationsIntegrationTests
|
||||||
|
* @see MockitoResetTestExecutionListenerWithMockitoBeanIntegrationTests
|
||||||
|
*/
|
||||||
|
// The MockVerificationExtension MUST be registered before the SpringExtension.
|
||||||
|
@ExtendWith(MockVerificationExtension.class)
|
||||||
|
@ExtendWith(SpringExtension.class)
|
||||||
|
@TestMethodOrder(MethodOrderer.MethodName.class)
|
||||||
|
class MockResetStrategiesIntegrationTests {
|
||||||
|
|
||||||
|
static PuzzleService puzzleServiceNoneStaticReference;
|
||||||
|
static PuzzleService puzzleServiceBeforeStaticReference;
|
||||||
|
static PuzzleService puzzleServiceAfterStaticReference;
|
||||||
|
|
||||||
|
|
||||||
|
@MockitoBean(name = "puzzleServiceNone", reset = MockReset.NONE)
|
||||||
|
PuzzleService puzzleServiceNone;
|
||||||
|
|
||||||
|
@MockitoBean(name = "puzzleServiceBefore", reset = MockReset.BEFORE)
|
||||||
|
PuzzleService puzzleServiceBefore;
|
||||||
|
|
||||||
|
@MockitoBean(name = "puzzleServiceAfter", reset = MockReset.AFTER)
|
||||||
|
PuzzleService puzzleServiceAfter;
|
||||||
|
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
void trackStaticReferences() {
|
||||||
|
puzzleServiceNoneStaticReference = this.puzzleServiceNone;
|
||||||
|
puzzleServiceBeforeStaticReference = this.puzzleServiceBefore;
|
||||||
|
puzzleServiceAfterStaticReference = this.puzzleServiceAfter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterAll
|
||||||
|
static void releaseStaticReferences() {
|
||||||
|
puzzleServiceNoneStaticReference = null;
|
||||||
|
puzzleServiceBeforeStaticReference = null;
|
||||||
|
puzzleServiceAfterStaticReference = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void test001(TestInfo testInfo) {
|
||||||
|
assertThat(puzzleServiceNone.getAnswer()).isNull();
|
||||||
|
assertThat(puzzleServiceBefore.getAnswer()).isNull();
|
||||||
|
assertThat(puzzleServiceAfter.getAnswer()).isNull();
|
||||||
|
|
||||||
|
stubAndTestMocks(testInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void test002(TestInfo testInfo) {
|
||||||
|
// Should not have been reset.
|
||||||
|
assertThat(puzzleServiceNone.getAnswer()).isEqualTo("none - test001");
|
||||||
|
|
||||||
|
// Should have been reset.
|
||||||
|
assertThat(puzzleServiceBefore.getAnswer()).isNull();
|
||||||
|
assertThat(puzzleServiceAfter.getAnswer()).isNull();
|
||||||
|
|
||||||
|
stubAndTestMocks(testInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stubAndTestMocks(TestInfo testInfo) {
|
||||||
|
String name = testInfo.getTestMethod().get().getName();
|
||||||
|
given(puzzleServiceNone.getAnswer()).willReturn("none - " + name);
|
||||||
|
assertThat(puzzleServiceNone.getAnswer()).isEqualTo("none - " + name);
|
||||||
|
|
||||||
|
given(puzzleServiceBefore.getAnswer()).willReturn("before - " + name);
|
||||||
|
assertThat(puzzleServiceBefore.getAnswer()).isEqualTo("before - " + name);
|
||||||
|
|
||||||
|
given(puzzleServiceAfter.getAnswer()).willReturn("after - " + name);
|
||||||
|
assertThat(puzzleServiceAfter.getAnswer()).isEqualTo("after - " + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PuzzleService {
|
||||||
|
|
||||||
|
String getAnswer();
|
||||||
|
}
|
||||||
|
|
||||||
|
static class MockVerificationExtension implements AfterEachCallback {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterEach(ExtensionContext context) throws Exception {
|
||||||
|
String name = context.getRequiredTestMethod().getName();
|
||||||
|
|
||||||
|
// Should not have been reset.
|
||||||
|
assertThat(puzzleServiceNoneStaticReference.getAnswer()).as("puzzleServiceNone").isEqualTo("none - " + name);
|
||||||
|
assertThat(puzzleServiceBeforeStaticReference.getAnswer()).as("puzzleServiceBefore").isEqualTo("before - " + name);
|
||||||
|
|
||||||
|
// Should have been reset.
|
||||||
|
assertThat(puzzleServiceAfterStaticReference.getAnswer()).as("puzzleServiceAfter").isNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -28,6 +28,7 @@ import static org.mockito.BDDMockito.given;
|
||||||
* @author Sam Brannen
|
* @author Sam Brannen
|
||||||
* @since 6.2
|
* @since 6.2
|
||||||
* @see MockitoResetTestExecutionListenerWithoutMockitoAnnotationsIntegrationTests
|
* @see MockitoResetTestExecutionListenerWithoutMockitoAnnotationsIntegrationTests
|
||||||
|
* @see MockResetStrategiesIntegrationTests
|
||||||
*/
|
*/
|
||||||
class MockitoResetTestExecutionListenerWithMockitoBeanIntegrationTests
|
class MockitoResetTestExecutionListenerWithMockitoBeanIntegrationTests
|
||||||
extends MockitoResetTestExecutionListenerWithoutMockitoAnnotationsIntegrationTests {
|
extends MockitoResetTestExecutionListenerWithoutMockitoAnnotationsIntegrationTests {
|
||||||
|
|
|
@ -42,6 +42,7 @@ import static org.mockito.Mockito.mock;
|
||||||
* @author Sam Brannen
|
* @author Sam Brannen
|
||||||
* @since 6.2
|
* @since 6.2
|
||||||
* @see MockitoResetTestExecutionListenerWithMockitoBeanIntegrationTests
|
* @see MockitoResetTestExecutionListenerWithMockitoBeanIntegrationTests
|
||||||
|
* @see MockResetStrategiesIntegrationTests
|
||||||
*/
|
*/
|
||||||
@SpringJUnitConfig
|
@SpringJUnitConfig
|
||||||
@TestMethodOrder(MethodOrderer.MethodName.class)
|
@TestMethodOrder(MethodOrderer.MethodName.class)
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
* 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.integration;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.context.event.ContextRefreshedEvent;
|
||||||
|
import org.springframework.context.event.EventListener;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
||||||
|
import org.springframework.test.context.bean.override.mockito.integration.MockitoBeanUsedDuringApplicationContextRefreshIntegrationTests.ContextRefreshedEventListener;
|
||||||
|
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.BDDMockito.then;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integration tests for {@link MockitoBean @MockitoBean} used during
|
||||||
|
* {@code ApplicationContext} refresh.
|
||||||
|
*
|
||||||
|
* @author Sam Brannen
|
||||||
|
* @author Yanming Zhou
|
||||||
|
* @since 6.2.1
|
||||||
|
*/
|
||||||
|
@SpringJUnitConfig(ContextRefreshedEventListener.class)
|
||||||
|
class MockitoBeanUsedDuringApplicationContextRefreshIntegrationTests {
|
||||||
|
|
||||||
|
@MockitoBean
|
||||||
|
ContextRefreshedEventProcessor eventProcessor;
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void test() {
|
||||||
|
// Ensure that the mock was invoked during ApplicationContext refresh
|
||||||
|
// and has not been reset in the interim.
|
||||||
|
then(eventProcessor).should().process(any(ContextRefreshedEvent.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
interface ContextRefreshedEventProcessor {
|
||||||
|
void process(ContextRefreshedEvent event);
|
||||||
|
}
|
||||||
|
|
||||||
|
// MUST be annotated with @Component, due to EventListenerMethodProcessor.isSpringContainerClass().
|
||||||
|
@Component
|
||||||
|
record ContextRefreshedEventListener(ContextRefreshedEventProcessor contextRefreshedEventProcessor) {
|
||||||
|
|
||||||
|
@EventListener
|
||||||
|
void onApplicationEvent(ContextRefreshedEvent event) {
|
||||||
|
this.contextRefreshedEventProcessor.process(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue