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.
|
||||
* @param mock the source mock
|
||||
* @return the reset type (never {@code null})
|
||||
* Get the {@link MockReset} strategy associated with the given mock.
|
||||
* @param mock the mock
|
||||
* @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) {
|
||||
MockingDetails mockingDetails = Mockito.mockingDetails(mock);
|
||||
|
|
|
@ -37,8 +37,18 @@ class MockitoBeans {
|
|||
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 {
|
||||
beanFactory.getBean(MockitoBeans.class).resetAll();
|
||||
beanFactory.getBean(MockitoBeans.class).resetAll(reset);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
// 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
|
||||
* @since 6.2
|
||||
* @see MockitoResetTestExecutionListenerWithoutMockitoAnnotationsIntegrationTests
|
||||
* @see MockResetStrategiesIntegrationTests
|
||||
*/
|
||||
class MockitoResetTestExecutionListenerWithMockitoBeanIntegrationTests
|
||||
extends MockitoResetTestExecutionListenerWithoutMockitoAnnotationsIntegrationTests {
|
||||
|
|
|
@ -42,6 +42,7 @@ import static org.mockito.Mockito.mock;
|
|||
* @author Sam Brannen
|
||||
* @since 6.2
|
||||
* @see MockitoResetTestExecutionListenerWithMockitoBeanIntegrationTests
|
||||
* @see MockResetStrategiesIntegrationTests
|
||||
*/
|
||||
@SpringJUnitConfig
|
||||
@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