86

I have a private method in my test class that constructs a commonly used Bar object. The Bar constructor calls someMethod() method in my mocked object:

private @Mock Foo mockedObject; // My mocked object
...

private Bar getBar() {
  Bar result = new Bar(mockedObject); // this calls mockedObject.someMethod()
}

In some of my test methods I want to check someMethod was also invoked by that particular test. Something like the following:

@Test
public void someTest() {
  Bar bar = getBar();

  // do some things

  verify(mockedObject).someMethod(); // <--- will fail
}

This fails, because the mocked object had someMethod invoked twice. I don't want my test methods to care about the side effects of my getBar() method, so would it be reasonable to reset my mock object at the end of getBar()?

private Bar getBar() {
  Bar result = new Bar(mockedObject); // this calls mockedObject.someMethod()
  reset(mockedObject); // <-- is this OK?
}

I ask, because the documentation suggests resetting mock objects is generally indicative of bad tests. However, this feels OK to me.

Alternative

The alternative choice seems to be calling:

verify(mockedObject, times(2)).someMethod();

which in my opinion forces each test to know about the expectations of getBar(), for no gain.

3 Answers 3

75

I believe this is one of the cases where using reset() is ok. The test you are writing is testing that "some things" triggers a single call to someMethod(). Writing the verify() statement with any different number of invocations can lead to confusion.

  • atLeastOnce() allows for false positives, which is a bad thing as you want your tests to always be correct.
  • times(2) prevents the false positive, but makes it seem like you are expecting two invocations rather than saying "i know the constructor adds one". Further more, if something changes in the constructor to add an extra call, the test now has a chance for a false positive. And removing the call would cause the test to fail because the test is now wrong instead of what is being tested is wrong.

By using reset() in the helper method, you avoid both of these issues. However, you need to be careful that it will also reset any stubbing you have done, so be warned. The major reason reset() is discouraged is to prevent

bar = mock(Bar.class);
//do stuff
verify(bar).someMethod();
reset(bar);
//do other stuff
verify(bar).someMethod2();

This is not what the OP is trying to do. The OP, I'm assuming, has a test that verifies the invocation in the constructor. For this test, the reset allows for isolating this single action and it's effect. This one of the few cases with reset() can be helpful as. The other options that don't use it all have cons. The fact that the OP made this post shows that he is thinking about the situation and not just blindly utilizing the reset method.

2
  • 20
    I wish Mockito provided resetInteractions() call to just forget the past interactions for the purpose of verify(..., times(...)) and keep the stubbing. This would make the test situations of {setup; act; verify;} that much easier to deal with. It would be {setup; resetInteractions; act; verify}
    – Arkadiy
    Aug 14, 2014 at 15:34
  • 14
    Actually since Mockito 2.1 it does provide a way to clear invocations without resetting the stubs: Mockito.clearInvocations(T... mocks) Mar 20, 2019 at 23:21
7

Smart Mockito users hardly use reset feature because they know it could be a sign of poor tests. Normally, you don't need to reset your mocks, just create new mocks for each test method.

Instead of reset() please consider writing simple, small and focused test methods over lengthy, over-specified tests. First potential code smell is reset() in the middle of the test method.

Extracted from the mockito docs.

My advise is that you try to avoid using reset(). In my opinion, if you call twice to someMethod, that should be tested (maybe it is a database access, or other long process that you want to take care about).

If you really don't care about that, you could use:

verify(mockedObject, atLeastOnce()).someMethod();

Note that this last could cause a false result, if you call someMethod from getBar, and not after (this is a wrong behaviour, but the test will not fail).

5
  • 2
    Yes, I've seen that exact quote (I linked to it from my question). Currently I'm yet to see a decent argument about why my example above is "bad". Can you supply one? Feb 27, 2013 at 9:51
  • If you need to reset your mock objects, it seems like you are trying to test too much stuff in your test. You can split it in two tests, testing smaller stuff. In any case, I don't know why you are verifying inside the getBar method, it's difficult to track what you are testing. I recommend you to design your test thinking in what your class should do (if you must call someMethod exactly two times, at least one time, only one time, never, etc.), and do every verification in the same place.
    – greuze
    Feb 27, 2013 at 11:25
  • I've edited my question to highlight that the problem persists even if I don't call verify in my private method (which I agree, probably doesn't belong there). I welcome your comments on whether your answer would change. Feb 27, 2013 at 13:07
  • There are a lot of good reasons to use reset, I wouldn't pay too much attention in this case to that mockito quote. You can have Spring's JUnit Class Runner when running a test suite cause unwanted interactions, especially if you're doing testing that involves mocked database calls or calls that involve private methods that you don't care to use reflection on. Sep 29, 2016 at 22:32
  • I usually find this a hard thing when I want to test multiple things, but JUnit simply does not offer any nice (!) way to parameterize tests. Unlike NUnit does for example with annotations. Oct 26, 2017 at 13:57
5

Absolutely not. As is so often the case, the difficulty you are having in writing a clean test is a major red flag about the design of your production code. In this case, the best solution is to refactor your code so that the constructor of Bar does not call any methods.

Constructors should construct, not execute logic. Take the return value of the method and pass it in as a constructor parameter.

new Bar(mockedObject);

becomes:

new Bar(mockedObject.someMethod());

If this would result in duplicating this logic in many places, consider creating a factory method which can be tested independently of your Bar object:

public Bar createBar(MockedObject mockedObject) {
    Object dependency = mockedObject.someMethod();
    // ...more logic that used to be in Bar constructor
    return new Bar(dependency);
}

If this refactoring is too difficult then using reset() is a good work around. But let's be clear - it is indicating that your code is poorly designed.

2

Not the answer you're looking for? Browse other questions tagged or ask your own question.