How to clean up mocks in spring tests when using Mockito

  1. About placing the reset after the test method

    I think reseting the mocks should better be done after the test method, as it implies there is indeed something that happened during the test that need to be cleaned.

    If the reset is done before the test method, I would feel unsure, what the heck happened before the test that should be reseted? What about about non-mocks object? Is there a reason (maybe there is) for it? If there's a reason why it's not mentioned in the code (for example the method name)? Et cetera.

  2. Not a fan of Spring based tests

    1. Background

      Using Spring is like giving up on unit testing a class ; with Spring you have less control on the test : isolation, instantiation, lifecycle, to cite a few of the looked property in a unit test. However in many cases Spring offer libraries and framework that are not that "transparent", for testing you better test the actual behavior of the whole stuff, like with Spring MVC, Spring Batch, etc.

      And crafting these tests is much more troublesome as it imposes in many cases the developer to craft integration tests to seriously test the behavior of the production code. As many developer don't know every detail about how the your code lives inside Spring, this can lead to many surprises to try to test a class with unit tests.

      But trouble continues, tests should be fast and small to give fast feedback to the developers (IDE plugin such as Infinitest are great for that), but tests with Spring are inherently more slow and more memory consuming. Which tends to run them less often and even avoid them totally on the local workstation ...to later discover on the CI server that they fail.

    2. Lifecycle with Mockito and Spring

      So when an integration test is crafted for a subsystem, you end up with many objects and obviously the collaborators, that are probably mocked. The lifecycle is controlled by the Spring Runner, but Mockito mocks are not. So you have to manage the mocks lifecycle yourself.

      Again about lifecycle during a project with Spring Batch we had some issues with residual effects on non mocks so we had two choices, make only one test method per test class or use the dirty context trick : @DirtiesContext(classMode=ClassMode.AFTER_EACH_TEST_METHOD). This lead to slower tests, higher memory consumption, but it was the best option we had. With this trick you won't have to reset Mockito mocks.

  3. A possible light in the dark

    I don't know well enough the project, but springockito can provide you some sugar about lifecycle. The annotation subproject seems to be even better : it seems to let Spring manage the lifecycle of the beans in the Spring container and to let the test control how mocks are being used. Still I have no experience with this tool, so there might be surprises.

As a disclaimer, I like Spring a lot, it offers many remarkable tool to simplify other framework usage, it can augment productivity, it helps the design, but like every tool the Humans invented there's always a rough edge (if not more...).

On a side note, this is interesting to see this problematic happen to be in a JUnit context, as JUnit instantiate the test class for each test method. If the test were based on TestNG then the approach might be a little different as TestNG creates only one instance of the test class, resting mock fields would be mandatory regardless of using Spring.


Old answer:

I'm not a huge fan of using Mockito mocks in a spring conxtext. But could you be looking for something like :

@After public void reset_mocks() {
    Mockito.reset(placeOrderService);
}

Spring Boot has @MockBean annotation which you can use to mock your service. You don't need to reset mocks manually any more. Just replace @Autowired by @MockBean:

@MockBean
private PlaceOrderService placeOrderService;

Instead of injecting the placeOrderService object, you should probably just let Mockito initialize it as a @Mock before each test, via something like this:

@Mock private PlaceOrderService placeOrderService;

@Before
public void setup() {
    MockitoAnnotations.initMocks(this);
}

As recommended in the Javadoc here: http://docs.mockito.googlecode.com/hg/latest/org/mockito/MockitoAnnotations.html

You can even put the @Before method in a superclass and just extend it for each test case class that uses @Mock objects.


Spring based test is hard to make fast and independed (as @Brice wrote). Here is a litle utility method for reset all mocks (you have to call it manually in every @Before method):

import org.mockito.Mockito;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.support.AopUtils;
import org.springframework.context.ApplicationContext;


public class MyTest {
    public void resetAll(ApplicationContext applicationContext) throws Exception {
        for (String name : applicationContext.getBeanDefinitionNames()) {
            Object bean = applicationContext.getBean(name);
            if (AopUtils.isAopProxy(bean) && bean instanceof Advised) {
                bean = ((Advised)bean).getTargetSource().getTarget();
            }
            if (Mockito.mockingDetails(bean).isMock()) {
                Mockito.reset(bean);
            }
        }
    }
}

As you see there is an iteration for all beans, check whether bean is mock or not, and reset the mock. I pay especially attention on call AopUtils.isAopProxy and ((Advised)bean).getTargetSource().getTarget(). If you bean contains an @Transactional annotation the mock of this bean always wrapped by spring into proxy object, so to reset or verify this mock you should unwrap it first. Otherwise you will get a UnfinishedVerificationException which can arise in different tests from time to time.

In my case AopUtils.isAopProxy is enough. But there are also AopUtils.isCglibProxy and AopUtils.isJdkDynamicProxy if you get troubles with proxying.

mockito is 1.10.19 spring-test is 3.2.2.RELEASE


Yet another way for resetting the mocks in the spring context.

https://github.com/Eedanna/mockito/issues/119#issuecomment-166823815

Assuming you're using Spring, you can easily implement this yourself by obtaining your ApplicationContext and then doing the following:

public static void resetMocks(ApplicationContext context) {
    for ( String name : context.getBeanDefinitionNames() ) {
        Object bean = context.getBean( name );
        if (new MockUtil().isMock( bean )) {
            Mockito.reset( bean );
        }
    }
}

https://github.com/Eedanna/mockito/issues/119

coupling the above code with @AfterAll should be a good way to clean up/reset the mocks.