This is more an idea for enhancement rather than an issue.

@MockBean and @SpyBean are designed in a way that using them amends the application context. Using them in any bigger application requires care and understanding how Spring context caching works and how to optimize tests structure to avoid creating many application contexts.

Perhaps there is an option to change the behavior of @MockBean and @SpyBean to replace beans with mocks/spies in application context for the specific test case, and then replace them back with original objects. This way, these annotations would not trigger creation of the new application context.

Such behavior has been implemented in https://github.com/antoinemeyer/mock-in-bean/. The biggest problem I see with this approach is executing tests in parallel.

Comment From: wilkinsona

I hadn't seen Mock in Bean before. Thanks for the pointer to it.

Perhaps there is an option to change the behavior of @MockBean and @SpyBean to replace beans with mocks/spies in application context for the specific test case … Such behavior has been implemented in https://github.com/antoinemeyer/mock-in-bean/

The behavior implemented by Mock in Bean seems to be subtly different. Rather than replacing beans with mocks/spies in the application context for the specific test, it replaces the value of a field in a bean for the duration of a test. While clever, I think this approach may come with too many caveats to include in Spring Boot itself. For example, I believe it only works when the item that you want to mock or spy is held in a field of a bean. It also requires this field to be mutated. That either impedes constructor-based injection and immutability or it requires reflective hacks to set a final field which may mess with memory visibility when multiple threads are involved, such as when testing with WebTestClient or TestRestTemplate.

One problem with replacing things at the bean level on a test-by-test basis is that we don't know where those beans have been injected or how they've been used while the context was being refreshed. This makes it very hard to swap in a mock or spy for the duration of a test in every place where that is needed. One option is to lazily proxy everything and then return the actual bean, a mock, or a spy, on a test-by-test basis but this brings with it a number of different problems.

I can certainly see the appeal of being able to mock and spy different beans in different tests without it resulting in many different application contexts being created. It's something that's come up more than once over the years but we've been unable to think of a good way to do it that doesn't introduce too many caveats and drawbacks. I'll leave this issue open so that we can think about it again.

Comment From: ramananrpn

Hey @wilkinsona @maciejwalkowiak , Good day! Im a new contributor looking to start contributing to this repo! Im happy to pick with this if you are looking for hands! Please let me know so that I can start with this with some of your insights . Or I'll be more happy if you helping me with anything to start with like good-first-issue to get familiar with code-base as Im a first time contributor!

Awaiting your response

Comment From: wilkinsona

Thanks for your interest in contributing, @ramananrpn. We don't know what, if anything, we can do to address this requirement so it isn't a great place to get started. I'm not sure if we have any at the moment, but please keep an eye out for unassigned issues labelled with ideal for contribution or, as your haven't contributed before, first-timers only.

Comment From: ramananrpn

Thanks @wilkinsona . Ill keep an eye in this

Comment From: fprochazka

I'm successfully using the following technique on my projects to avoid this problem:

@AutoConfiguration
public class TestOverridesConfiguration {
    @Primary
    @Bean
    public ExternalService externalServiceMock(@Qualifier("externalService") final ExternalService real) {
        return Mockito.mock(ExternalService.class, AdditionalAnswers.delegatesTo(real));
    }

with a custom TestExecutionListener that resets the mocks around every test.

It can probably be improved in several ways, but it is good enough for me for now. E.g. this could be wrapped semi-automatically with BeanPostProcessor, or something like that, similarly to what Spring is doing with Hibernate's EntityManager.

Comment From: Mobe91

Hey @wilkinsona I'd be interested in your opinion on @fprochazka 's approach (also see his blog post). Do you see any caveats that people should be aware of before following this?

Comment From: wilkinsona

It feels similar to @MockBean and @SpyBean as it will change the application context. As long as you use TestOverridesConfiguration everywhere then that won't affect the number of contexts create by running the test suite. It probably makes that consistent configuration easier than @MockBean and @SpyBean do.

Comment From: edyda99

There is a library that tackles this problem MockInBean If you want to integrate it with Spring Boot, We can contact the library owner. Or I ( familiar with both Spring framework and this library ) can do a POC under your guide! @wilkinsona Hope to hear from you soon!!

Comment From: wilkinsona

Mock In Bean has already been discussed in this very issue. Please see the opening comment and my first reply.

Comment From: edyda99

Not sure How I missed that! Thanks for your reply

Comment From: antoinemeyer

@wilkinsona , getting back to that original comment. You mentioned:

One option is to lazily proxy everything and then return the actual bean, a mock, or a spy, on a test-by-test basis but this brings with it a number of different problems.

Could you please elaborate on those different problems you are concerned with? (I've been working on a solution that can handle parallel test suites by proxying the fields while keeping track of all original beans/mocks per thread)

Comment From: wilkinsona

Proxying isn't without its problems and can change behavior in subtle and unpredictable ways, particularly with final classes, classes that have already been proxied, classes that call methods on themselves, and so on. Proxying every bean in the context brings those possible changes in behavior to every bean in the context and does so only for tests.

Comment From: wilkinsona

@bclozel another one please for evaluation on the Framework side in the context of @MockitoBean and @MockitoSpyBean.

Comment From: wilkinsona

I"m going to close this one as @MockBean and @SpyBean are now Spring Framework features where they're named @MockitoBean and @MockitoSpyBean respectively.