From 51956fad89bc19abd33f5406ad246226c791435a Mon Sep 17 00:00:00 2001 From: Sam Brannen <104798+sbrannen@users.noreply.github.com> Date: Fri, 29 Nov 2024 11:27:02 +0100 Subject: [PATCH] =?UTF-8?q?Test=20MockReset=20strategy=20for=20@=E2=81=A0M?= =?UTF-8?q?ockitoSpyBean?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As a follow up to commit 0088b9c7f8, this commit introduces an integration test which verifies that a spy created via @⁠MockitoSpyBean using the MockReset.AFTER strategy is not reset between the refresh of the ApplicationContext and the first use of the spy within a @⁠Test method. See gh-33941 See gh-33986 --- ...icationContextRefreshIntegrationTests.java | 5 + ...icationContextRefreshIntegrationTests.java | 98 +++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/integration/MockitoSpyBeanUsedDuringApplicationContextRefreshIntegrationTests.java diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/integration/MockitoBeanUsedDuringApplicationContextRefreshIntegrationTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/integration/MockitoBeanUsedDuringApplicationContextRefreshIntegrationTests.java index 1e171b2adef4..edfe3a817895 100644 --- a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/integration/MockitoBeanUsedDuringApplicationContextRefreshIntegrationTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/integration/MockitoBeanUsedDuringApplicationContextRefreshIntegrationTests.java @@ -17,6 +17,7 @@ package org.springframework.test.context.bean.override.mockito.integration; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.context.event.EventListener; @@ -25,6 +26,7 @@ import org.springframework.test.context.bean.override.mockito.integration.MockitoBeanUsedDuringApplicationContextRefreshIntegrationTests.ContextRefreshedEventListener; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.then; @@ -35,6 +37,7 @@ * @author Sam Brannen * @author Yanming Zhou * @since 6.2.1 + * @see MockitoSpyBeanUsedDuringApplicationContextRefreshIntegrationTests */ @SpringJUnitConfig(ContextRefreshedEventListener.class) class MockitoBeanUsedDuringApplicationContextRefreshIntegrationTests { @@ -45,6 +48,8 @@ class MockitoBeanUsedDuringApplicationContextRefreshIntegrationTests { @Test void test() { + assertThat(Mockito.mockingDetails(eventProcessor).isMock()).as("isMock").isTrue(); + assertThat(Mockito.mockingDetails(eventProcessor).isSpy()).as("isSpy").isFalse(); // Ensure that the mock was invoked during ApplicationContext refresh // and has not been reset in the interim. then(eventProcessor).should().process(any(ContextRefreshedEvent.class)); diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/integration/MockitoSpyBeanUsedDuringApplicationContextRefreshIntegrationTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/integration/MockitoSpyBeanUsedDuringApplicationContextRefreshIntegrationTests.java new file mode 100644 index 000000000000..ed36075ad774 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/integration/MockitoSpyBeanUsedDuringApplicationContextRefreshIntegrationTests.java @@ -0,0 +1,98 @@ +/* + * 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.AfterAll; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +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.MockitoSpyBean; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.same; +import static org.mockito.BDDMockito.then; + +/** + * Integration tests for {@link MockitoSpyBean @MockitoSpyBean} used during + * {@code ApplicationContext} refresh. + * + * @author Sam Brannen + * @since 6.2.1 + * @see MockitoBeanUsedDuringApplicationContextRefreshIntegrationTests + */ +@SpringJUnitConfig +class MockitoSpyBeanUsedDuringApplicationContextRefreshIntegrationTests { + + static ContextRefreshedEvent contextRefreshedEvent; + + @MockitoSpyBean + ContextRefreshedEventProcessor eventProcessor; + + + @AfterAll + static void clearStaticField() { + contextRefreshedEvent = null; + } + + @Test + void test() { + assertThat(Mockito.mockingDetails(eventProcessor).isSpy()).as("isSpy").isTrue(); + // Ensure that the spy was invoked during ApplicationContext refresh + // and has not been reset in the interim. + then(eventProcessor).should().process(same(contextRefreshedEvent)); + } + + + @Configuration + @Import(ContextRefreshedEventListener.class) + static class Config { + + @Bean + ContextRefreshedEventProcessor eventProcessor() { + // Cannot be a lambda expression, since Mockito cannot create a spy for a lambda. + return new ContextRefreshedEventProcessor() { + + @Override + public void process(ContextRefreshedEvent event) { + contextRefreshedEvent = event; + } + }; + } + } + + 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); + } + } + +}