-
Notifications
You must be signed in to change notification settings - Fork 38.4k
Support @MockitoBean
at the type level on test classes
#33925
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Here's a couple of other cases that Including mocks via additional config (not just other annotations): @TestConfiguration
@MockBean({
MyServiceWithOutOfProcessSideEffectsOnBoot.class,
MyPreemptiveClientAuthTokenFetcher.class
})
@EnableSomething
@Import(OtherStuff.class)
public class SharedTestConfiguration { }
@SpringTestAnnotation
@Import({SharedTestConfiguration.class, SomeOtherConfig.class})
public class SomeTests { If one of these mocks were autowired, stub behaviour could then be specified. @SpringTestAnnotation
@MockBean(MyServiceWithOutOfProcessSideEffectsOnBoot.class) // actually declared elsewhere
public class SomeTests {
@Autowired private MyServiceWithOutOfProcessSideEffectsOnBoot myService;
@Test
public void onlyThisTestSpecificallyWantsToStubIt() {
doReturn(true).when(myService).isSomething();
} |
@MockitoBean
support
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
@MockitoBean
support@MockitoBean
at the class level on test classes
We appreciate the constructive feedback we have received here as well as in #33934, and in light of that we are considering introducing support for declaring If we choose to do so, that would allow In terms of declaring multiple types to mock (with different mock configurations), that could be supported by allowing In any case, The following demonstrates what such a programming model might look like. @SpringJUnitConfig
// Register one or more types to mock
@MockitoBean(types = { ExampleService.class, AnotherService.class })
class MyIntegrationTests {
// Optionally inject the mocks for stubbing and verification.
//
// @Autowired
// ExampleService exampleService;
//
// @Autowired
// AnotherService anotherService;
@Test
void test() {
// ...
}
} For reuse of the mock configuration across the test suite, teams could introduce composed annotations such as the following. @Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@MockitoBean(types = { ExampleService.class, AnotherService.class })
public @interface SharedMocks {
} That would allow @SpringJUnitConfig
// Reuse @MockitoBean declarations via a composed annotation:
@SharedMocks
class MyIntegrationTests {
// Optionally inject the mocks for stubbing and verification.
//
// @Autowired
// ExampleService exampleService;
//
// @Autowired
// AnotherService anotherService;
@Test
void test() {
// ...
}
} Another option would be to limit one type per @SpringJUnitConfig
@MockitoBean(type = ExampleService.class)
@MockitoBean(type = AnotherService.class)
class MyIntegrationTests {
// Optionally inject the mocks for stubbing and verification.
//
// @Autowired
// ExampleService exampleService;
//
// @Autowired
// AnotherService anotherService;
@Test
void test() {
// ...
}
}
As I mentioned previously, we are not yet committed to implementing this feature, but we are considering it. Thus, if you are interested in this GitHub issue or #33934, we would appreciate your feedback on whether the above proposal would satisfy your needs to reuse As a side note, we would also be interested in knowing how many Spring Boot users have traditionally declared Thanks, Sam |
@MockitoBean
at the class level on test classes@MockitoBean
at the type level on test classes
The composed annotation example would solve my cases. |
Thanks for the update @sbrannen ! Here are some responses to topics raised in your comment:
|
Maybe it's a stupid idea, but isn't it better to create a new annotation for it? Because I feel it is misuse for @MockitoBeanClass(value=ExampleService.class, spy=SpiedService.class)
@MockitoBeanClass(OtherExampleService.class) |
Hi and many thanks for providing an open discussion on this topic 👍 In my current project, we used to heavily rely on For example: @SpringBootTest
@Import({...configs...})
@MockBeans({@MockBean(ClassA.class), @MockBean(ClassB.class)})
public @interface BookingTest {} @BookingTest
class BookingPaymentIT {
@Test
void testSomething() {
// ...
}
} As opposed to: @SpringBootTest
@Import({...configs...})
class BookingPaymentIT {
@MockitoBean
private ClassA classAMock;
@MockitoBean
private ClassB classBMock;
@Test
void testSomething() {
// ...
}
} Also this reduced redundancy, because we could reuse the mock "definitions". Although with Spring Boot 3.4.x this may also be achieved in a different way, we found the old way to be very convenient and straightforward. |
Another shorter annotation variant could be something like: @MockitoBean(type = {FirstService.class, SecondService.class})
class MyIntegrationTests {
} |
On my projects, we heavily rely on @Component
public class ClientAMockBean {
@MockBean
private ClientA clientA;
public void stubInitCatalog(){
when(clientA).getCatalog()).thenReturn(initCatalog()));
}
public void stubGetLiveProduct(){
when(clientA).getProduct(any()).thenReturn(new Product(UUID.randomUUID()));
}
public void stubPurchaseProduct(UUID id){
when(clientA).buyProduct(eq(id)).thenReturn(new Transaction(UUID.randomUUID(),id));
}
//...
} public class ITCommonCallbacks implements BeforeEachCallback, BeforeAllCallback{
private ClientAMockBean clientAMockBean;
@Override
public void beforeAll(ExtensionContext context) {
ApplicationContext springContext = SpringExtension.getApplicationContext(context);
clientAMockBean = springContext.getBean(ClientAMockBean.class);
}
@Override
public void beforeEach(ExtensionContext extensionContext) {
clientAMockBean.stubInitCatalog();
}
} @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ExtendWith({ ITCommonCallbacks.class })
class ProductPurchaseIT {
@Autowired
private ClientAMockBean clientAMockBean;
@Autowired
AppServer server;
@Test
void shouldBuyProduct(){
var user = server.initUserCases();
var productA = user.getFirstCatalogProduct();
clientAMockBean.stubPurchaseProduct(productA.id())
//...
} Ideally I would have liked to be able to have @TestComponent("clientAMockBean")
public class ClientAMockBean {
@Autowired
private ClientA clientA;
public void stubInitCatalog(){
when(clientA).getCatalog()).thenReturn(initCatalog()));
}
public void stubGetLiveProduct(){
when(clientA).getProduct(any()).thenReturn(new Product(UUID.randomUUID()));
}
public void stubPurchaseProduct(UUID id){
when(clientA).buyProduct(eq(id)).thenReturn(new Transaction(UUID.randomUUID(),id));
}
//...
} @Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@MockitoBean(types = { ClientA.class, AnotherService.class })
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ExtendWith({ ITCommonCallbacks.class })
public @interface SpringBootIT {
} @SpringBootIT
class ProductPurchaseIT {
@Autowired
private ClientAMockBean clientAMockBean;
@Autowired
AppServer server;
@Test
void shouldBuyProduct(){
var user = server.initUserCases();
var productA = user.getFirstCatalogProduct();
clientAMockBean.stubPurchaseProduct(productA.id())
//...
} |
Chiming in to say that the ability to declare I currently have several annotations in my project, such as I'm currently declaring I am eagerly hoping that you decide to support this. |
Hi everybody, Thanks for all of the feedback! The current plan is to support This issue is currently assigned to the Regards, Sam |
Proof of Concept See spring-projectsgh-33925
Proof of Concept See spring-projectsgh-33925
Update: current work on this can be viewed in the following feature branch.
|
Prior to this commit, @MockitoBean could only be declared on fields within test classes, which prevented developers from being able to easily reuse mock configuration across a test suite. With this commit, @MockitoBean is now supported at the type level on test classes, their superclasses, and interfaces implemented by those classes. @MockitoBean is also supported on enclosing classes for @Nested test classes, their superclasses, and interfaces implemented by those classes, while honoring @NestedTestConfiguration semantics. In addition, @MockitoBean: - has a new `types` attribute that can be used to declare the type or types to mock when @MockitoBean is declared at the type level - can be declared as a repeatable annotation at the type level - can be declared as a meta-annotation on a custom composed annotation which can be reused across a test suite (see the @SharedMocks example in the reference manual) To support these new features, this commit also includes the following changes. - The `field` property in BeanOverrideHandler is now @Nullable. - BeanOverrideProcessor has a new `default` createHandlers() method which is invoked when a @BeanOverride annotation is found at the type level. - MockitoBeanOverrideProcessor implements the new createHandlers() method. - The internal findHandlers() method in BeanOverrideHandler has been completely overhauled. - The @MockitoBean and @MockitoSpyBean section of the reference manual has been completely overhauled. Closes spring-projectsgh-33925
This new feature was released today with Spring Framework 6.2.2. Please check out the updated documentation for details and examples! |
Hi, there! |
Address newly deprecated code issues. Address ambiguous `any()` issue that now appears and breaks tests. The `@MockBean` is no longer available and we must instead use `@MockitoBean`. see: spring-projects/spring-framework#29917 (comment) see: spring-projects/spring-framework#33925 see: https://docs.spring.io/spring-framework/reference/testing/annotations/integration-spring/annotation-mockitobean.html
Changes made to the Bean Override search algorithms in commit 9181cce resulted in a regression that caused tests to start failing due to duplicate BeanOverrideHandlers under the following circumstances. - An enclosing class (typically a top-level test class) declares a @BeanOverride such as @MockitoBean. - An inner class is declared in that enclosing class. - A @Nested test class which extends that inner class is declared in the same enclosing class. The reason for the duplicate detection is that the current search algorithm visits the common enclosing class twice. To address that, this commit revises the search algorithm in BeanOverrideHandler so that enclosing classes are only visited once. See gh-33925 Closes gh-34324
Thanks for mentioning that. I created #34408 for us to investigate the feasibility of supporting Regards, Sam |
Just to note that unlike |
Prior to this commit, @MockitoSpyBean could only be declared on fields within test classes, which prevented developers from being able to easily reuse spy configuration across a test suite. With this commit, @MockitoSpyBean is now supported at the type level on test classes, their superclasses, and interfaces implemented by those classes. @MockitoSpyBean is also supported on enclosing classes for @Nested test classes, their superclasses, and interfaces implemented by those classes, while honoring @NestedTestConfiguration semantics. In addition, @MockitoSpyBean: - has a new `types` attribute that can be used to declare the type or types to spy when @MockitoSpyBean is declared at the type level - can be declared as a repeatable annotation at the type level - can be declared as a meta-annotation on a custom composed annotation which can be reused across a test suite (see the @SharedSpies example in the reference manual) To support these new features, this commit also includes the following changes. - MockitoSpyBeanOverrideProcessor has been revised to support @MockitoSpyBean at the type level. - The "Bean Overriding in Tests" and "@MockitoBean and @MockitoSpyBean" sections of the reference manual have been fully revised. See spring-projectsgh-34408 Closes spring-projectsgh-33925
Prior to this commit, @MockitoSpyBean could only be declared on fields within test classes, which prevented developers from being able to easily reuse spy configuration across a test suite. With this commit, @MockitoSpyBean is now supported at the type level on test classes, their superclasses, and interfaces implemented by those classes. @MockitoSpyBean is also supported on enclosing classes for @Nested test classes, their superclasses, and interfaces implemented by those classes, while honoring @NestedTestConfiguration semantics. In addition, @MockitoSpyBean: - has a new `types` attribute that can be used to declare the type or types to spy when @MockitoSpyBean is declared at the type level - can be declared as a repeatable annotation at the type level - can be declared as a meta-annotation on a custom composed annotation which can be reused across a test suite (see the @SharedSpies example in the reference manual) To support these new features, this commit also includes the following changes. - MockitoSpyBeanOverrideProcessor has been revised to support @MockitoSpyBean at the type level. - The "Bean Overriding in Tests" and "@MockitoBean and @MockitoSpyBean" sections of the reference manual have been fully revised. See gh-34408 Closes gh-33925
Are there plans to fix it? |
No there are not, see #33934 |
hey guys, someone mentioned here spring-projects/spring-boot#43348 that this is fixed. and on stackoverflow i found this way to implement MockitoBean on class level: @MockitoBean(types={F.class,B.class}) |
MockitoBean
is designed to replace the now-deprecatedMockBean
annotation.MockBean
could target either a field or a type. Currently,MockitoBean
can only target a field. This issue proposes adding the support to target types toMockitoBean
, similar toMockBean
.(pasting some prose from #29917 comments)
Here's some Kotlin code to help demonstrate the point above:
vs
The text was updated successfully, but these errors were encountered: