Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using @MockBean in tests forces reloading of Application Context

I have several integration tests running on Spring Framework that extend the base class called BaseITCase.
Like this:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {AppCacheConfiguration.class, TestConfiguration.class}, loader = SpringBootContextLoader.class)
@Transactional
@WebMvcTest
public abstract class BaseITCase{...}
...
public class UserControllerTest extends BaseITCase {...}

The problem is that one of the test has several declarations of: @MockBean inside of it and the moment this test executed, Spring recreates context and the tests that follows this one sometimes use wrong beans(from the context created exactly for the test with @MockBean). I found out about that just by checking that beans have different hashcodes.

It becomes really critical when I use @EventListener. Because listeners for wrong context(context of the test class that has already finished execution) are invoked and I have wrong beans there.

Is there any workaround for that?

I tried to move all @MockBean declarations to basic class and it worked fine because new context is not created. But, it makes basic class too heavy. Also, I tried to make a dirty context for this test, but then the next test fails with message that context has already been closed.

like image 590
dvelopp Avatar asked Aug 09 '17 09:08

dvelopp


People also ask

Which class is used to test Spring MVC rest Web application?

We create the test data by using a test data builder class. Configure our mock object to return the created test data when its findAll() method is invoked.

What is the use of ContextConfiguration?

Annotation Type ContextConfiguration. @ContextConfiguration defines class-level metadata that is used to determine how to load and configure an ApplicationContext for integration tests.

How to mock application context in Spring Boot?

Spring Boot's @MockBean Annotation We can use the @MockBean to add mock objects to the Spring application context. The mock will replace any existing bean of the same type in the application context. If no bean of the same type is defined, a new one will be added.

What is the difference between @mock and @MockBean?

Main difference between @MockBean and @Mock annotation is that @MockBean creates mock and injects it into Application Context, while @Mock annotation only creates it, if you want to inject it, you can do it manually or with @InjectMocks annotation, however injection is being done to the class not whole Application ...


1 Answers

The reason is that the spring configuration for the test having the @MockBean is different from the rest of the tests, so the spring framework cannot cache previously used context and needs to load it again. Here you can find a more detailed explanation: https://github.com/spring-projects/spring-boot/issues/10015

As you said, if you move the mock bean to the parent class the context doesn't get reloaded, which makes sense as the bean configuration remains the same.

A possible workaround is defining your mock bean as a simple mock and injecting it manually where is required.

For instance, UserController has a dependency on Foo:

public class UserControllerTest extends BaseITCase {

    private Foo foo = Mockito.mock(Foo.class);

    @Autowired
    private UserController userController;

    @Before
    public void setUp() {
        super.setup();

        this.userController.setFoo(foo);
    }
}

@Component
public class UserController {

    private Foo foo;

    @Autowired
    public void setFoo(final Foo foo) {
        this.foo = foo;
    }
}

Hope this helps.

like image 52
Daniel Camarasa Avatar answered Nov 15 '22 15:11

Daniel Camarasa