forked from spring-projects/spring-framework
-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Log warning when one Bean Override overrides another Bean Override
It is currently possible for one Bean Override to override another logically equivalent Bean Override. For example, a @TestBean can override a @MockitoBean, and vice versa. In fact, it's also possible for a @MockitoBean to override another @MockitoBean, for a @TestBean to override a @TestBean, etc. However, there may be viable use cases for one override overriding another override. For example, one may have a need to spy on a bean created by a @TestBean factory method. In light of that, we do not prohibit one Bean Override from overriding another Bean Override; however, with this commit we now log a warning to help developers diagnose issues in case such an override is unintentional. For example, given the following test class, where TestConfig registers a single bean of type MyService named "myService"... @SpringJUnitConfig(TestConfig.class) class MyTests { @TestBean(methodName = "example.TestUtils#createMyService") MyService testService; @MockitoBean MyService mockService; @Test void test() { // ... } } ... running that test class results in a log message similar to the following, which has been formatted for readability. WARN - Bean with name 'myService' was overridden by multiple handlers: [ [TestBeanOverrideHandler@44b21f9f field = example.MyService example.MyTests.testService, beanType = example.MyService, beanName = [null], strategy = REPLACE_OR_CREATE ], [MockitoBeanOverrideHandler@7ee8130e field = example.MyService example.MyTests.mockService, beanType = example.MyService, beanName = [null], strategy = REPLACE_OR_CREATE, reset = AFTER, extraInterfaces = set[[empty]], answers = RETURNS_DEFAULTS, serializable = false ] ] NOTE: The last registered BeanOverrideHandler wins. In the above example, that means that @MockitoBean overrides @TestBean, resulting in a Mockito mock for the MyService bean in the test's ApplicationContext. Closes spring-projectsgh-34056
- Loading branch information
Showing
5 changed files
with
252 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
97 changes: 97 additions & 0 deletions
97
...st/context/bean/override/mockito/MockitoBeanDuplicateTypeReplacementIntegrationTests.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
/* | ||
* 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.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.test.context.bean.override.example.ExampleService; | ||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.mockito.BDDMockito.given; | ||
import static org.springframework.test.context.bean.override.mockito.MockReset.AFTER; | ||
import static org.springframework.test.context.bean.override.mockito.MockReset.BEFORE; | ||
import static org.springframework.test.mockito.MockitoAssertions.assertIsMock; | ||
|
||
/** | ||
* Integration tests for {@link MockitoBean @MockitoBean} where duplicate mocks | ||
* are created to replace the same existing bean, selected by-type. | ||
* | ||
* <p>In other words, this test class demonstrates how one {@code @MockitoBean} | ||
* can silently override another {@code @MockitoBean}. | ||
* | ||
* @author Sam Brannen | ||
* @since 6.2.1 | ||
* @see <a href="https://github.com/spring-projects/spring-framework/issues/34056">gh-34056</a> | ||
* @see MockitoBeanDuplicateTypeCreationIntegrationTests | ||
* @see MockitoSpyBeanDuplicateTypeIntegrationTests | ||
*/ | ||
@SpringJUnitConfig | ||
public class MockitoBeanDuplicateTypeReplacementIntegrationTests { | ||
|
||
@MockitoBean(reset = AFTER) | ||
ExampleService mock1; | ||
|
||
@MockitoBean(reset = BEFORE) | ||
ExampleService mock2; | ||
|
||
@Autowired | ||
List<ExampleService> services; | ||
|
||
/** | ||
* One could argue that we would ideally expect an exception to be thrown when | ||
* two competing mocks are created to replace the same existing bean; however, | ||
* we currently only log a warning in such cases. | ||
* <p>This method therefore asserts the status quo in terms of behavior. | ||
* <p>And the log can be manually checked to verify that an appropriate | ||
* warning was logged. | ||
*/ | ||
@Test | ||
void onlyOneMockShouldHaveBeenCreated() { | ||
// Currently logs something similar to the following. | ||
// | ||
// WARN - Bean with name 'exampleService' was overridden by multiple handlers: | ||
// [MockitoBeanOverrideHandler@5478ce1e ..., MockitoBeanOverrideHandler@5edc70ed ...] | ||
|
||
// Last one wins: there's only one physical mock | ||
assertThat(services).containsExactly(mock2); | ||
assertThat(mock1).isSameAs(mock2); | ||
|
||
assertIsMock(mock2); | ||
assertThat(MockReset.get(mock2)).as("MockReset").isEqualTo(BEFORE); | ||
|
||
assertThat(mock2.greeting()).isNull(); | ||
given(mock2.greeting()).willReturn("mocked"); | ||
assertThat(mock2.greeting()).isEqualTo("mocked"); | ||
} | ||
|
||
|
||
@Configuration | ||
static class Config { | ||
|
||
@Bean | ||
ExampleService exampleService() { | ||
return () -> "@Bean"; | ||
} | ||
} | ||
|
||
} |
103 changes: 103 additions & 0 deletions
103
...work/test/context/bean/override/mockito/MockitoBeanOverridesTestBeanIntegrationTests.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
/* | ||
* 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.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.test.context.bean.override.convention.TestBean; | ||
import org.springframework.test.context.bean.override.example.ExampleService; | ||
import org.springframework.test.context.bean.override.example.RealExampleService; | ||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.mockito.BDDMockito.given; | ||
import static org.springframework.test.mockito.MockitoAssertions.assertIsMock; | ||
|
||
/** | ||
* Integration tests for Bean Overrides where a {@link MockitoBean @MockitoBean} | ||
* overrides a {@link TestBean @TestBean} when trying to replace the same existing | ||
* bean, selected by-type. | ||
* | ||
* <p>In other words, this test class demonstrates how one Bean Override can | ||
* silently override another Bean Override. | ||
* | ||
* @author Sam Brannen | ||
* @since 6.2.1 | ||
* @see <a href="https://github.com/spring-projects/spring-framework/issues/34056">gh-34056</a> | ||
* @see MockitoBeanDuplicateTypeCreationIntegrationTests | ||
* @see MockitoBeanDuplicateTypeReplacementIntegrationTests | ||
*/ | ||
@SpringJUnitConfig | ||
public class MockitoBeanOverridesTestBeanIntegrationTests { | ||
|
||
@TestBean | ||
ExampleService testService; | ||
|
||
@MockitoBean | ||
ExampleService mockService; | ||
|
||
@Autowired | ||
List<ExampleService> services; | ||
|
||
|
||
static ExampleService testService() { | ||
return new RealExampleService("@TestBean"); | ||
} | ||
|
||
|
||
/** | ||
* One could argue that we would ideally expect an exception to be thrown when | ||
* two competing overrides are created to replace the same existing bean; however, | ||
* we currently only log a warning in such cases. | ||
* <p>This method therefore asserts the status quo in terms of behavior. | ||
* <p>And the log can be manually checked to verify that an appropriate | ||
* warning was logged. | ||
*/ | ||
@Test | ||
void mockitoBeanShouldOverrideTestBean() { | ||
// Currently logs something similar to the following. | ||
// | ||
// WARN - Bean with name 'exampleService' was overridden by multiple handlers: | ||
// [TestBeanOverrideHandler@770beef5 ..., MockitoBeanOverrideHandler@6dd1f638 ...] | ||
|
||
// Last override wins... | ||
assertThat(services).containsExactly(mockService); | ||
assertThat(testService).isSameAs(mockService); | ||
|
||
assertIsMock(mockService); | ||
|
||
assertThat(mockService.greeting()).isNull(); | ||
given(mockService.greeting()).willReturn("mocked"); | ||
assertThat(mockService.greeting()).isEqualTo("mocked"); | ||
} | ||
|
||
|
||
@Configuration | ||
static class Config { | ||
|
||
@Bean | ||
ExampleService exampleService() { | ||
return () -> "@Bean"; | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters