Context not being reused in tests when MockBeans are used

See original GitHub issue

Hi!

I would like to start a discussion about the mechanism of caching application contexts that are using @MockBeans across spring boot tests.

I was following this guide about testing the web layer: https://spring.io/guides/gs/testing-web/ and I was surprised that application context was build independently for every test class. I was hoping it will be cached to decrease test time, but I was wrong.

I’ve created small sample app to show this. Here is pull request: https://github.com/spring-projects/spring-boot-issues/pull/56

There’s one @Service (FooBarService) used by controller (FooBarApplication) in two methods. And two tests that have some expectations about the web layer (as indicated by the @WebMvcTest annotation). Those two tests work on the same context - the only difference is distinct mocks. But if you do mvn clean test you’ll see the spring banner twice, which means that context for the web layer is created two times.

I’ve done some research and debugging and found that this is caused by including the @MockBean and @SpyBean definitions in the key for context cache (through the MockitoContextCustomizer that is a part of MergedContextConfiguration).

Well… After rethinking all of this I can understand that from technical point of view those contexts are not the same, because mock@1 and mock@2 are not equal. But still I feel that my assumption that context should be reused is what users could expect. Especially if we realize that @WebMvcTest is used to reduce the test time by not starting tomcat. Having this goal in mind it’s hard to accept the extra time of repeated context building.

I’m not sure if such a change is a bug fixing or enhancement. I feel that this behaviour is bug but you can discuss it.

For now my workaround (or maybe it’s official way to do this?) is to create an abstract test class with all @MockBeans definitions, something like:

@RunWith(SpringRunner.class)
@WebMvcTest
public abstract class AbstractTest {
    protected @MockBean FooBarService service;
}

public class FooTest extends AbstractTest {...}

But maybe it’s better to consider an approach in which context is build once and mocks are replaced in beforeTestClass and afterTestClass methods of TestExecutionListener? And to avoid refreshing dependencies in every class referencing those mocked beans maybe it’s ok to generate some beanToMockProxy object, that will be autowired in controllers and will hold a reference to mockito mock (created per test class), proxing all the methods to that mock? I don’t know… I’m just thinking loud.

Cheers, Alek

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Reactions:15
  • Comments:26 (13 by maintainers)

github_iconTop GitHub Comments

12reactions
alebarcommented, Oct 25, 2016

Thank you all very much for taking care of that 😃

I know that the issue is closed but I’d like to follow up on the topic of context caching. You’ve fixed the mechanism of calculating cache’s keys based on mock definitions. But I’m not convinced for the general idea of including mocks in that key 😦

Please consider one more example that I’ve provided in issues repo. The PR is here: https://github.com/spring-projects/spring-boot-issues/pull/59

In that example I have two tests, that are testing separate features and are independent. And they are using the same WebMvc context - the only difference is in mocks used in both. Despite that, spring is creating two contexts (which may take long time) instead of one (run mvn test to see banner twice).

My feeling is that developer’s decision of what to mock and when shouldn’t have impact on context creation. I’d expect in that case one application context, shared between tests and with @MockBeans created and injected into context only for one test class.

What do you think?

4reactions
surapuramakhilcommented, Jul 27, 2020

@philwebb In reality for any project there would be at least of 40 integration tests (bare minimum) and its may not be possible for having integration tests without mocking. Or maintaining same mocks for all tests. Reloading context for 40 times Will easily make test time 1hr that very bad for testing For engineering perspective, It does not mandatory required for rebuilding context on configuration change. If proxies are maintained - this can be handled very easily. Like proxy holder contains service holder and that pointing address can be changed on mocking. We don’t have to recreate all beans or change objects of each bean where mocked DI is there. And Unit FW will take care of copy cache and modify that proxy bean so scope can only to be on that test I suggest or feel like this should have to be new feature of spring boot test - which will be required for many people who are using spring.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Using @MockBean in tests forces reloading of Application ...
I tried to move all @MockBean declarations to basic class and it worked fine because new context is not created. But, it makes...
Read more >
@Mock vs. @MockBean When Testing Spring Boot ... - rieckpil
Be aware that whenever you use @MockBean Spring can't reuse an already existing context that includes the actual bean of this type. This...
Read more >
A quick guide to Spring Tests optimization - 10Pines | Blog
The context is reused by default unless you do some things like: Using Mockito or Mockk annotations like @MockBean or @SpyBean, annotations that ......
Read more >
46. Testing - Spring
@MockBean cannot be used to mock the behavior of a bean that's exercised during application context refresh. By the time the test is...
Read more >
Service integration tests not reusing application context (fixed!)
Spring used to reuse the application context for all web ... test class had a different set of mock beans, Spring was not...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found