Browse Source
Prior to this commit, @MockitoBean and @MockitoSpyBean could be
declared on fields or at the type level (on test classes and test
interfaces), but not on constructor parameters. Consequently, a test
class could not use constructor injection for bean overrides.
To address that, this commit introduces support for @MockitoBean and
@MockitoSpyBean on constructor parameters in JUnit Jupiter test
classes. Specifically, the Bean Override infrastructure has been
overhauled to support constructor parameters as declaration sites and
injection points alongside fields, and the SpringExtension now
recognizes composed @BeanOverride annotations on constructor
parameters in supportsParameter() and resolves them properly in
resolveParameter(). Note, however, that this support has not been
introduced for @TestBean.
For example, the following which uses field injection:
@SpringJUnitConfig(TestConfig.class)
class BeanOverrideTests {
@MockitoBean
CustomService customService;
// tests...
}
Can now be rewritten to use constructor injection:
@SpringJUnitConfig(TestConfig.class)
class BeanOverrideTests {
private final CustomService customService;
BeanOverrideTests(@MockitoBean CustomService customService) {
this.customService = customService;
}
// tests...
}
With Kotlin this can be achieved even more succinctly via a compact
constructor declaration:
@SpringJUnitConfig(TestConfig::class)
class BeanOverrideTests(@MockitoBean val customService: CustomService) {
// tests...
}
Of course, if one is a fan of so-called "test records", that can also
be achieved succinctly with a Java record:
@SpringJUnitConfig(TestConfig.class)
record BeanOverrideTests(@MockitoBean CustomService customService) {
// tests...
}
Closes gh-36096
main
31 changed files with 1730 additions and 112 deletions
@ -0,0 +1,175 @@
@@ -0,0 +1,175 @@
|
||||
/* |
||||
* Copyright 2002-present 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.constructor; |
||||
|
||||
import org.junit.jupiter.api.Nested; |
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.beans.factory.annotation.Qualifier; |
||||
import org.springframework.context.ApplicationContext; |
||||
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.bean.override.example.RealExampleService; |
||||
import org.springframework.test.context.bean.override.mockito.MockitoBean; |
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; |
||||
import org.springframework.test.mockito.MockitoAssertions; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* Integration tests for {@link MockitoBean @MockitoBean} that use by-name lookup |
||||
* on constructor parameters. |
||||
* |
||||
* @author Sam Brannen |
||||
* @since 7.1 |
||||
* @see <a href="https://github.com/spring-projects/spring-framework/issues/36096">gh-36096</a> |
||||
* @see org.springframework.test.context.bean.override.mockito.MockitoBeanByNameLookupTestMethodScopedExtensionContextIntegrationTests |
||||
*/ |
||||
@SpringJUnitConfig |
||||
class MockitoBeanByNameLookupForConstructorParametersIntegrationTests { |
||||
|
||||
final ExampleService service0A; |
||||
|
||||
final ExampleService service0B; |
||||
|
||||
final ExampleService service0C; |
||||
|
||||
final ExampleService nonExisting; |
||||
|
||||
|
||||
MockitoBeanByNameLookupForConstructorParametersIntegrationTests( |
||||
@MockitoBean ExampleService s0A, |
||||
@MockitoBean(name = "s0B") ExampleService service0B, |
||||
@MockitoBean @Qualifier("s0C") ExampleService service0C, |
||||
@MockitoBean("nonExistingBean") ExampleService nonExisting) { |
||||
|
||||
this.service0A = s0A; |
||||
this.service0B = service0B; |
||||
this.service0C = service0C; |
||||
this.nonExisting = nonExisting; |
||||
} |
||||
|
||||
|
||||
@Test |
||||
void parameterNameIsUsedAsBeanName(ApplicationContext ctx) { |
||||
assertThat(this.service0A) |
||||
.satisfies(MockitoAssertions::assertIsMock) |
||||
.isSameAs(ctx.getBean("s0A")); |
||||
|
||||
assertThat(this.service0A.greeting()).as("mocked greeting").isNull(); |
||||
} |
||||
|
||||
@Test |
||||
void explicitBeanNameOverridesParameterName(ApplicationContext ctx) { |
||||
assertThat(this.service0B) |
||||
.satisfies(MockitoAssertions::assertIsMock) |
||||
.isSameAs(ctx.getBean("s0B")); |
||||
|
||||
assertThat(this.service0B.greeting()).as("mocked greeting").isNull(); |
||||
} |
||||
|
||||
@Test |
||||
void qualifierIsUsedToResolveByName(ApplicationContext ctx) { |
||||
assertThat(this.service0C) |
||||
.satisfies(MockitoAssertions::assertIsMock) |
||||
.isSameAs(ctx.getBean("s0C")); |
||||
|
||||
assertThat(this.service0C.greeting()).as("mocked greeting").isNull(); |
||||
} |
||||
|
||||
@Test |
||||
void mockIsCreatedWhenNoBeanExistsWithProvidedName(ApplicationContext ctx) { |
||||
assertThat(this.nonExisting) |
||||
.satisfies(MockitoAssertions::assertIsMock) |
||||
.isSameAs(ctx.getBean("nonExistingBean")); |
||||
|
||||
assertThat(this.nonExisting.greeting()).as("mocked greeting").isNull(); |
||||
} |
||||
|
||||
|
||||
@Nested |
||||
class NestedTests { |
||||
|
||||
@Autowired |
||||
@Qualifier("s0A") |
||||
ExampleService localService0A; |
||||
|
||||
@Autowired |
||||
@Qualifier("nonExistingBean") |
||||
ExampleService localNonExisting; |
||||
|
||||
final ExampleService nestedNonExisting; |
||||
|
||||
|
||||
NestedTests(@MockitoBean("nestedNonExistingBean") ExampleService nestedNonExisting) { |
||||
this.nestedNonExisting = nestedNonExisting; |
||||
} |
||||
|
||||
|
||||
@Test |
||||
void mockFromEnclosingClassIsAccessibleViaAutowiring(ApplicationContext ctx) { |
||||
assertThat(this.localService0A) |
||||
.satisfies(MockitoAssertions::assertIsMock) |
||||
.isSameAs(service0A) |
||||
.isSameAs(ctx.getBean("s0A")); |
||||
|
||||
assertThat(this.localService0A.greeting()).as("mocked greeting").isNull(); |
||||
} |
||||
|
||||
@Test |
||||
void mockForNonExistingBeanFromEnclosingClassIsAccessibleViaAutowiring(ApplicationContext ctx) { |
||||
assertThat(this.localNonExisting) |
||||
.satisfies(MockitoAssertions::assertIsMock) |
||||
.isSameAs(nonExisting) |
||||
.isSameAs(ctx.getBean("nonExistingBean")); |
||||
|
||||
assertThat(this.localNonExisting.greeting()).as("mocked greeting").isNull(); |
||||
} |
||||
|
||||
@Test |
||||
void nestedConstructorParameterIsMockedWhenNoBeanExistsWithProvidedName(ApplicationContext ctx) { |
||||
assertThat(this.nestedNonExisting) |
||||
.satisfies(MockitoAssertions::assertIsMock) |
||||
.isSameAs(ctx.getBean("nestedNonExistingBean")); |
||||
|
||||
assertThat(this.nestedNonExisting.greeting()).as("mocked greeting").isNull(); |
||||
} |
||||
} |
||||
|
||||
|
||||
@Configuration(proxyBeanMethods = false) |
||||
static class Config { |
||||
|
||||
@Bean |
||||
ExampleService s0A() { |
||||
return new RealExampleService("prod s0A"); |
||||
} |
||||
|
||||
@Bean |
||||
ExampleService s0B() { |
||||
return new RealExampleService("prod s0B"); |
||||
} |
||||
|
||||
@Bean |
||||
ExampleService s0C() { |
||||
return new RealExampleService("prod s0C"); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,55 @@
@@ -0,0 +1,55 @@
|
||||
/* |
||||
* Copyright 2002-present 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.constructor; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.test.context.bean.override.example.ExampleService; |
||||
import org.springframework.test.context.bean.override.mockito.MockitoBean; |
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.mockito.Mockito.times; |
||||
import static org.mockito.Mockito.verify; |
||||
import static org.mockito.Mockito.verifyNoMoreInteractions; |
||||
import static org.mockito.Mockito.when; |
||||
import static org.springframework.test.mockito.MockitoAssertions.assertIsMock; |
||||
|
||||
/** |
||||
* Integration tests for {@link MockitoBean @MockitoBean} that use by-type lookup |
||||
* on constructor parameters in a Java record. |
||||
* |
||||
* @author Sam Brannen |
||||
* @since 7.1 |
||||
* @see <a href="https://github.com/spring-projects/spring-framework/issues/36096">gh-36096</a> |
||||
*/ |
||||
@SpringJUnitConfig |
||||
record MockitoBeanByTypeLookupForConstructorParametersIntegrationRecordTests( |
||||
@MockitoBean ExampleService exampleService) { |
||||
|
||||
@Test |
||||
void test() { |
||||
assertIsMock(this.exampleService); |
||||
|
||||
when(this.exampleService.greeting()).thenReturn("Mocked greeting"); |
||||
|
||||
assertThat(this.exampleService.greeting()).isEqualTo("Mocked greeting"); |
||||
verify(this.exampleService, times(1)).greeting(); |
||||
verifyNoMoreInteractions(this.exampleService); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,207 @@
@@ -0,0 +1,207 @@
|
||||
/* |
||||
* Copyright 2002-present 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.constructor; |
||||
|
||||
import org.junit.jupiter.api.Nested; |
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.beans.factory.NoUniqueBeanDefinitionException; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.beans.factory.annotation.Qualifier; |
||||
import org.springframework.context.ApplicationContext; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Configuration; |
||||
import org.springframework.core.annotation.Order; |
||||
import org.springframework.test.context.bean.override.example.CustomQualifier; |
||||
import org.springframework.test.context.bean.override.example.ExampleService; |
||||
import org.springframework.test.context.bean.override.example.RealExampleService; |
||||
import org.springframework.test.context.bean.override.mockito.MockitoBean; |
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; |
||||
import org.springframework.test.mockito.MockitoAssertions; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; |
||||
import static org.mockito.ArgumentMatchers.anyInt; |
||||
import static org.mockito.Mockito.times; |
||||
import static org.mockito.Mockito.verify; |
||||
import static org.mockito.Mockito.verifyNoMoreInteractions; |
||||
import static org.mockito.Mockito.when; |
||||
import static org.springframework.test.mockito.MockitoAssertions.assertIsMock; |
||||
|
||||
/** |
||||
* Integration tests for {@link MockitoBean @MockitoBean} that use by-type lookup |
||||
* on constructor parameters. |
||||
* |
||||
* @author Sam Brannen |
||||
* @since 7.1 |
||||
* @see <a href="https://github.com/spring-projects/spring-framework/issues/36096">gh-36096</a> |
||||
* @see org.springframework.test.context.bean.override.mockito.MockitoBeanByTypeLookupIntegrationTests |
||||
*/ |
||||
@SpringJUnitConfig |
||||
class MockitoBeanByTypeLookupForConstructorParametersIntegrationTests { |
||||
|
||||
final AnotherService serviceIsNotABean; |
||||
|
||||
final ExampleService anyNameForService; |
||||
|
||||
final StringBuilder ambiguous; |
||||
|
||||
final StringBuilder ambiguousMeta; |
||||
|
||||
|
||||
MockitoBeanByTypeLookupForConstructorParametersIntegrationTests( |
||||
@MockitoBean AnotherService serviceIsNotABean, |
||||
@MockitoBean ExampleService anyNameForService, |
||||
@MockitoBean @Qualifier("prefer") StringBuilder ambiguous, |
||||
@MockitoBean @CustomQualifier StringBuilder ambiguousMeta) { |
||||
|
||||
this.serviceIsNotABean = serviceIsNotABean; |
||||
this.anyNameForService = anyNameForService; |
||||
this.ambiguous = ambiguous; |
||||
this.ambiguousMeta = ambiguousMeta; |
||||
} |
||||
|
||||
|
||||
@Test |
||||
void mockIsCreatedWhenNoCandidateIsFound() { |
||||
assertIsMock(this.serviceIsNotABean); |
||||
|
||||
when(this.serviceIsNotABean.hello()).thenReturn("Mocked hello"); |
||||
|
||||
assertThat(this.serviceIsNotABean.hello()).isEqualTo("Mocked hello"); |
||||
verify(this.serviceIsNotABean, times(1)).hello(); |
||||
verifyNoMoreInteractions(this.serviceIsNotABean); |
||||
} |
||||
|
||||
@Test |
||||
void overrideIsFoundByType(ApplicationContext ctx) { |
||||
assertThat(this.anyNameForService) |
||||
.satisfies(MockitoAssertions::assertIsMock) |
||||
.isSameAs(ctx.getBean("example")) |
||||
.isSameAs(ctx.getBean(ExampleService.class)); |
||||
|
||||
when(this.anyNameForService.greeting()).thenReturn("Mocked greeting"); |
||||
|
||||
assertThat(this.anyNameForService.greeting()).isEqualTo("Mocked greeting"); |
||||
verify(this.anyNameForService, times(1)).greeting(); |
||||
verifyNoMoreInteractions(this.anyNameForService); |
||||
} |
||||
|
||||
@Test |
||||
void overrideIsFoundByTypeAndDisambiguatedByQualifier(ApplicationContext ctx) { |
||||
assertThat(this.ambiguous) |
||||
.satisfies(MockitoAssertions::assertIsMock) |
||||
.isSameAs(ctx.getBean("ambiguous2")); |
||||
|
||||
assertThatExceptionOfType(NoUniqueBeanDefinitionException.class) |
||||
.isThrownBy(() -> ctx.getBean(StringBuilder.class)) |
||||
.satisfies(ex -> assertThat(ex.getBeanNamesFound()).containsOnly("ambiguous1", "ambiguous2")); |
||||
|
||||
assertThat(this.ambiguous).isEmpty(); |
||||
assertThat(this.ambiguous.substring(0)).isNull(); |
||||
verify(this.ambiguous, times(1)).length(); |
||||
verify(this.ambiguous, times(1)).substring(anyInt()); |
||||
verifyNoMoreInteractions(this.ambiguous); |
||||
} |
||||
|
||||
@Test |
||||
void overrideIsFoundByTypeAndDisambiguatedByMetaQualifier(ApplicationContext ctx) { |
||||
assertThat(this.ambiguousMeta) |
||||
.satisfies(MockitoAssertions::assertIsMock) |
||||
.isSameAs(ctx.getBean("ambiguous1")); |
||||
|
||||
assertThatExceptionOfType(NoUniqueBeanDefinitionException.class) |
||||
.isThrownBy(() -> ctx.getBean(StringBuilder.class)) |
||||
.satisfies(ex -> assertThat(ex.getBeanNamesFound()).containsOnly("ambiguous1", "ambiguous2")); |
||||
|
||||
assertThat(this.ambiguousMeta).isEmpty(); |
||||
assertThat(this.ambiguousMeta.substring(0)).isNull(); |
||||
verify(this.ambiguousMeta, times(1)).length(); |
||||
verify(this.ambiguousMeta, times(1)).substring(anyInt()); |
||||
verifyNoMoreInteractions(this.ambiguousMeta); |
||||
} |
||||
|
||||
|
||||
@Nested |
||||
class NestedTests { |
||||
|
||||
@Autowired |
||||
ExampleService localAnyNameForService; |
||||
|
||||
final NestedService nestedService; |
||||
|
||||
|
||||
NestedTests(@MockitoBean NestedService nestedService) { |
||||
this.nestedService = nestedService; |
||||
} |
||||
|
||||
|
||||
@Test |
||||
void mockFromEnclosingClassConstructorParameterIsAccessibleViaAutowiring(ApplicationContext ctx) { |
||||
assertThat(this.localAnyNameForService) |
||||
.satisfies(MockitoAssertions::assertIsMock) |
||||
.isSameAs(anyNameForService) |
||||
.isSameAs(ctx.getBean("example")) |
||||
.isSameAs(ctx.getBean(ExampleService.class)); |
||||
} |
||||
|
||||
@Test |
||||
void nestedConstructorParameterIsAMock() { |
||||
assertIsMock(this.nestedService); |
||||
|
||||
when(this.nestedService.hello()).thenReturn("Nested hello"); |
||||
assertThat(this.nestedService.hello()).isEqualTo("Nested hello"); |
||||
verify(this.nestedService).hello(); |
||||
verifyNoMoreInteractions(this.nestedService); |
||||
} |
||||
} |
||||
|
||||
|
||||
public interface AnotherService { |
||||
|
||||
String hello(); |
||||
} |
||||
|
||||
public interface NestedService { |
||||
|
||||
String hello(); |
||||
} |
||||
|
||||
@Configuration(proxyBeanMethods = false) |
||||
static class Config { |
||||
|
||||
@Bean("example") |
||||
ExampleService bean1() { |
||||
return new RealExampleService("Production hello"); |
||||
} |
||||
|
||||
@Bean("ambiguous1") |
||||
@Order(1) |
||||
@CustomQualifier |
||||
StringBuilder bean2() { |
||||
return new StringBuilder("bean2"); |
||||
} |
||||
|
||||
@Bean("ambiguous2") |
||||
@Order(2) |
||||
@Qualifier("prefer") |
||||
StringBuilder bean3() { |
||||
return new StringBuilder("bean3"); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,165 @@
@@ -0,0 +1,165 @@
|
||||
/* |
||||
* Copyright 2002-present 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.constructor; |
||||
|
||||
import org.junit.jupiter.api.Nested; |
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.beans.factory.annotation.Qualifier; |
||||
import org.springframework.context.ApplicationContext; |
||||
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.bean.override.example.RealExampleService; |
||||
import org.springframework.test.context.bean.override.mockito.MockitoSpyBean; |
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; |
||||
import org.springframework.test.mockito.MockitoAssertions; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.mockito.Mockito.verify; |
||||
import static org.mockito.Mockito.verifyNoMoreInteractions; |
||||
|
||||
/** |
||||
* Integration tests for {@link MockitoSpyBean @MockitoSpyBean} that use by-name |
||||
* lookup on constructor parameters. |
||||
* |
||||
* @author Sam Brannen |
||||
* @since 7.1 |
||||
* @see <a href="https://github.com/spring-projects/spring-framework/issues/36096">gh-36096</a> |
||||
* @see org.springframework.test.context.bean.override.mockito.MockitoSpyBeanByNameLookupTestMethodScopedExtensionContextIntegrationTests |
||||
*/ |
||||
@SpringJUnitConfig |
||||
class MockitoSpyBeanByNameLookupForConstructorParametersIntegrationTests { |
||||
|
||||
final ExampleService service1; |
||||
|
||||
final ExampleService service2; |
||||
|
||||
final ExampleService service3; |
||||
|
||||
|
||||
MockitoSpyBeanByNameLookupForConstructorParametersIntegrationTests( |
||||
@MockitoSpyBean ExampleService s1, |
||||
@MockitoSpyBean("s2") ExampleService service2, |
||||
@MockitoSpyBean @Qualifier("s3") ExampleService service3) { |
||||
|
||||
this.service1 = s1; |
||||
this.service2 = service2; |
||||
this.service3 = service3; |
||||
} |
||||
|
||||
|
||||
@Test |
||||
void parameterNameIsUsedAsBeanName(ApplicationContext ctx) { |
||||
assertThat(this.service1) |
||||
.satisfies(MockitoAssertions::assertIsSpy) |
||||
.isSameAs(ctx.getBean("s1")); |
||||
|
||||
assertThat(this.service1.greeting()).isEqualTo("prod 1"); |
||||
verify(this.service1).greeting(); |
||||
verifyNoMoreInteractions(this.service1); |
||||
} |
||||
|
||||
@Test |
||||
void explicitBeanNameOverridesParameterName(ApplicationContext ctx) { |
||||
assertThat(this.service2) |
||||
.satisfies(MockitoAssertions::assertIsSpy) |
||||
.isSameAs(ctx.getBean("s2")); |
||||
|
||||
assertThat(this.service2.greeting()).isEqualTo("prod 2"); |
||||
verify(this.service2).greeting(); |
||||
verifyNoMoreInteractions(this.service2); |
||||
} |
||||
|
||||
@Test |
||||
void qualifierIsUsedToResolveByName(ApplicationContext ctx) { |
||||
assertThat(this.service3) |
||||
.satisfies(MockitoAssertions::assertIsSpy) |
||||
.isSameAs(ctx.getBean("s3")); |
||||
|
||||
assertThat(this.service3.greeting()).isEqualTo("prod 3"); |
||||
verify(this.service3).greeting(); |
||||
verifyNoMoreInteractions(this.service3); |
||||
} |
||||
|
||||
|
||||
@Nested |
||||
class NestedTests { |
||||
|
||||
@Autowired |
||||
@Qualifier("s1") |
||||
ExampleService localService1; |
||||
|
||||
final ExampleService nestedSpy; |
||||
|
||||
|
||||
NestedTests(@MockitoSpyBean("s4") ExampleService nestedSpy) { |
||||
this.nestedSpy = nestedSpy; |
||||
} |
||||
|
||||
|
||||
@Test |
||||
void spyFromEnclosingClassIsAccessibleViaAutowiring(ApplicationContext ctx) { |
||||
assertThat(this.localService1) |
||||
.satisfies(MockitoAssertions::assertIsSpy) |
||||
.isSameAs(service1) |
||||
.isSameAs(ctx.getBean("s1")); |
||||
|
||||
assertThat(this.localService1.greeting()).isEqualTo("prod 1"); |
||||
verify(this.localService1).greeting(); |
||||
verifyNoMoreInteractions(this.localService1); |
||||
} |
||||
|
||||
@Test |
||||
void nestedConstructorParameterIsASpy(ApplicationContext ctx) { |
||||
assertThat(this.nestedSpy) |
||||
.satisfies(MockitoAssertions::assertIsSpy) |
||||
.isSameAs(ctx.getBean("s4")); |
||||
|
||||
assertThat(this.nestedSpy.greeting()).isEqualTo("prod 4"); |
||||
verify(this.nestedSpy).greeting(); |
||||
verifyNoMoreInteractions(this.nestedSpy); |
||||
} |
||||
} |
||||
|
||||
|
||||
@Configuration(proxyBeanMethods = false) |
||||
static class Config { |
||||
|
||||
@Bean |
||||
ExampleService s1() { |
||||
return new RealExampleService("prod 1"); |
||||
} |
||||
|
||||
@Bean |
||||
ExampleService s2() { |
||||
return new RealExampleService("prod 2"); |
||||
} |
||||
|
||||
@Bean |
||||
ExampleService s3() { |
||||
return new RealExampleService("prod 3"); |
||||
} |
||||
|
||||
@Bean |
||||
ExampleService s4() { |
||||
return new RealExampleService("prod 4"); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,213 @@
@@ -0,0 +1,213 @@
|
||||
/* |
||||
* Copyright 2002-present 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.constructor; |
||||
|
||||
import org.junit.jupiter.api.Nested; |
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.beans.factory.annotation.Qualifier; |
||||
import org.springframework.context.ApplicationContext; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Configuration; |
||||
import org.springframework.core.annotation.Order; |
||||
import org.springframework.test.context.bean.override.example.CustomQualifier; |
||||
import org.springframework.test.context.bean.override.example.ExampleService; |
||||
import org.springframework.test.context.bean.override.example.RealExampleService; |
||||
import org.springframework.test.context.bean.override.mockito.MockitoSpyBean; |
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; |
||||
import org.springframework.test.mockito.MockitoAssertions; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatException; |
||||
import static org.mockito.Mockito.verify; |
||||
import static org.mockito.Mockito.verifyNoMoreInteractions; |
||||
|
||||
/** |
||||
* Integration tests for {@link MockitoSpyBean @MockitoSpyBean} that use by-type |
||||
* lookup on constructor parameters. |
||||
* |
||||
* @author Sam Brannen |
||||
* @since 7.1 |
||||
* @see <a href="https://github.com/spring-projects/spring-framework/issues/36096">gh-36096</a> |
||||
* @see org.springframework.test.context.bean.override.mockito.MockitoSpyBeanByTypeLookupIntegrationTests |
||||
*/ |
||||
@SpringJUnitConfig |
||||
class MockitoSpyBeanByTypeLookupForConstructorParametersIntegrationTests { |
||||
|
||||
final ExampleService anyNameForService; |
||||
|
||||
final StringHolder ambiguous; |
||||
|
||||
final StringHolder ambiguousMeta; |
||||
|
||||
|
||||
MockitoSpyBeanByTypeLookupForConstructorParametersIntegrationTests( |
||||
@MockitoSpyBean ExampleService anyNameForService, |
||||
@MockitoSpyBean @Qualifier("prefer") StringHolder ambiguous, |
||||
@MockitoSpyBean @CustomQualifier StringHolder ambiguousMeta) { |
||||
|
||||
this.anyNameForService = anyNameForService; |
||||
this.ambiguous = ambiguous; |
||||
this.ambiguousMeta = ambiguousMeta; |
||||
} |
||||
|
||||
|
||||
@Test |
||||
void overrideIsFoundByType(ApplicationContext ctx) { |
||||
assertThat(this.anyNameForService) |
||||
.satisfies(MockitoAssertions::assertIsSpy) |
||||
.isSameAs(ctx.getBean("example")) |
||||
.isSameAs(ctx.getBean(ExampleService.class)); |
||||
|
||||
assertThat(this.anyNameForService.greeting()).isEqualTo("Production hello"); |
||||
verify(this.anyNameForService).greeting(); |
||||
verifyNoMoreInteractions(this.anyNameForService); |
||||
} |
||||
|
||||
@Test |
||||
void overrideIsFoundByTypeAndDisambiguatedByQualifier(ApplicationContext ctx) { |
||||
assertThat(this.ambiguous) |
||||
.satisfies(MockitoAssertions::assertIsSpy) |
||||
.isSameAs(ctx.getBean("ambiguous2")); |
||||
|
||||
assertThatException() |
||||
.isThrownBy(() -> ctx.getBean(StringHolder.class)) |
||||
.withMessageEndingWith("but found 2: ambiguous1,ambiguous2"); |
||||
|
||||
assertThat(this.ambiguous.getValue()).isEqualTo("bean3"); |
||||
assertThat(this.ambiguous.size()).isEqualTo(5); |
||||
verify(this.ambiguous).getValue(); |
||||
verify(this.ambiguous).size(); |
||||
verifyNoMoreInteractions(this.ambiguous); |
||||
} |
||||
|
||||
@Test |
||||
void overrideIsFoundByTypeAndDisambiguatedByMetaQualifier(ApplicationContext ctx) { |
||||
assertThat(this.ambiguousMeta) |
||||
.satisfies(MockitoAssertions::assertIsSpy) |
||||
.isSameAs(ctx.getBean("ambiguous1")); |
||||
|
||||
assertThatException() |
||||
.isThrownBy(() -> ctx.getBean(StringHolder.class)) |
||||
.withMessageEndingWith("but found 2: ambiguous1,ambiguous2"); |
||||
|
||||
assertThat(this.ambiguousMeta.getValue()).isEqualTo("bean2"); |
||||
assertThat(this.ambiguousMeta.size()).isEqualTo(5); |
||||
verify(this.ambiguousMeta).getValue(); |
||||
verify(this.ambiguousMeta).size(); |
||||
verifyNoMoreInteractions(this.ambiguousMeta); |
||||
} |
||||
|
||||
|
||||
@Nested |
||||
class NestedTests { |
||||
|
||||
@Autowired |
||||
ExampleService localAnyNameForService; |
||||
|
||||
final AnotherService nestedSpy; |
||||
|
||||
|
||||
NestedTests(@MockitoSpyBean AnotherService nestedSpy) { |
||||
this.nestedSpy = nestedSpy; |
||||
} |
||||
|
||||
|
||||
@Test |
||||
void spyFromEnclosingClassConstructorParameterIsAccessibleViaAutowiring(ApplicationContext ctx) { |
||||
assertThat(this.localAnyNameForService) |
||||
.satisfies(MockitoAssertions::assertIsSpy) |
||||
.isSameAs(anyNameForService) |
||||
.isSameAs(ctx.getBean("example")) |
||||
.isSameAs(ctx.getBean(ExampleService.class)); |
||||
|
||||
assertThat(this.localAnyNameForService.greeting()).isEqualTo("Production hello"); |
||||
verify(this.localAnyNameForService).greeting(); |
||||
verifyNoMoreInteractions(this.localAnyNameForService); |
||||
} |
||||
|
||||
@Test |
||||
void nestedConstructorParameterIsASpy(ApplicationContext ctx) { |
||||
assertThat(this.nestedSpy) |
||||
.satisfies(MockitoAssertions::assertIsSpy) |
||||
.isSameAs(ctx.getBean("anotherService")) |
||||
.isSameAs(ctx.getBean(AnotherService.class)); |
||||
|
||||
assertThat(this.nestedSpy.hello()).isEqualTo("Another hello"); |
||||
verify(this.nestedSpy).hello(); |
||||
verifyNoMoreInteractions(this.nestedSpy); |
||||
} |
||||
} |
||||
|
||||
|
||||
interface AnotherService { |
||||
|
||||
String hello(); |
||||
} |
||||
|
||||
static class StringHolder { |
||||
|
||||
private final String value; |
||||
|
||||
StringHolder(String value) { |
||||
this.value = value; |
||||
} |
||||
|
||||
public String getValue() { |
||||
return this.value; |
||||
} |
||||
|
||||
public int size() { |
||||
return this.value.length(); |
||||
} |
||||
} |
||||
|
||||
@Configuration(proxyBeanMethods = false) |
||||
static class Config { |
||||
|
||||
@Bean("example") |
||||
ExampleService bean1() { |
||||
return new RealExampleService("Production hello"); |
||||
} |
||||
|
||||
@Bean("ambiguous1") |
||||
@Order(1) |
||||
@CustomQualifier |
||||
StringHolder bean2() { |
||||
return new StringHolder("bean2"); |
||||
} |
||||
|
||||
@Bean("ambiguous2") |
||||
@Order(2) |
||||
@Qualifier("prefer") |
||||
StringHolder bean3() { |
||||
return new StringHolder("bean3"); |
||||
} |
||||
|
||||
@Bean |
||||
AnotherService anotherService() { |
||||
return new AnotherService() { |
||||
@Override |
||||
public String hello() { |
||||
return "Another hello"; |
||||
} |
||||
}; |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,20 @@
@@ -0,0 +1,20 @@
|
||||
/* |
||||
* Copyright 2002-present 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.typelevel; |
||||
|
||||
interface ConstructorService01 extends Service { |
||||
} |
||||
@ -0,0 +1,44 @@
@@ -0,0 +1,44 @@
|
||||
/* |
||||
* Copyright 2002-present 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.Test |
||||
|
||||
import org.springframework.test.context.bean.override.example.ExampleService |
||||
import org.springframework.test.context.bean.override.mockito.MockitoBean |
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig |
||||
import org.springframework.test.mockito.MockitoAssertions.assertIsMock |
||||
|
||||
/** |
||||
* Integration tests for [@MockitoBean][MockitoBean] that use by-type lookup |
||||
* on constructor parameters in a Kotlin data class. |
||||
* |
||||
* @author Sam Brannen |
||||
* @since 7.1 |
||||
* @see <a href="https://github.com/spring-projects/spring-framework/issues/36096">gh-36096</a> |
||||
* @see MockitoBeanByTypeLookupForConstructorParametersIntegrationKotlinTests |
||||
*/ |
||||
@SpringJUnitConfig |
||||
data class MockitoBeanByTypeLookupForConstructorParametersIntegrationKotlinDataClassTests( |
||||
@MockitoBean val exampleService: ExampleService) { |
||||
|
||||
@Test |
||||
fun test() { |
||||
assertIsMock(exampleService) |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,44 @@
@@ -0,0 +1,44 @@
|
||||
/* |
||||
* Copyright 2002-present 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.Test |
||||
|
||||
import org.springframework.test.context.bean.override.example.ExampleService |
||||
import org.springframework.test.context.bean.override.mockito.MockitoBean |
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig |
||||
import org.springframework.test.mockito.MockitoAssertions.assertIsMock |
||||
|
||||
/** |
||||
* Integration tests for [@MockitoBean][MockitoBean] that use by-type lookup |
||||
* on constructor parameters in Kotlin. |
||||
* |
||||
* @author Sam Brannen |
||||
* @since 7.1 |
||||
* @see <a href="https://github.com/spring-projects/spring-framework/issues/36096">gh-36096</a> |
||||
* @see org.springframework.test.context.bean.override.mockito.MockitoBeanByNameLookupTestMethodScopedExtensionContextIntegrationTests |
||||
*/ |
||||
@SpringJUnitConfig |
||||
class MockitoBeanByTypeLookupForConstructorParametersIntegrationKotlinTests( |
||||
@MockitoBean val exampleService: ExampleService) { |
||||
|
||||
@Test |
||||
fun test() { |
||||
assertIsMock(exampleService) |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue