58

I have a JUnit class with different methods to perform different tests.

I use Mockito to create a spy on real instance, and then override some method which is not relevant to the actual test I perform.

Is there a way, just for the sake of cleaning up after me in case some other tests that run after my tests also use the same instances and might execute a mocked method they didn't ask to mock, to un-mock a method?

say I have a spy object called 'wareHouseSpy'

say I overriden the method isSomethingMissing :

doReturn(false).when(wareHouseSpy).isSomethingMissing()

What will be the right way to un-override, and bring things back to normal on the spy i.e make the next invokation of isSomethingMissing to run the real method?

something like

doReturn(Mockito.RETURN_REAL_METHOD).when(wareHouseSpy).isSomethingSpy()

or maybe

Mockito.unmock(wareHouseSpy)

Who knows? I couldn't find nothing in that area

Thanks!

Assaf

user1045740
  • 581
  • 1
  • 4
  • 3
  • 1
    "I use Mockito to create a spy on real instance, and then override some method which is not relevant to the actual test I perform." I don't understand, then why are you mocking it if you don't need it? – Liviu T. Nov 14 '11 at 14:36
  • Is the wareHouse in your example a singleton or something? Are these "other tests" in the same class or are you talking about a completely different set of tests? – jhericks Nov 18 '11 at 16:29

7 Answers7

62

I think

Mockito.reset(wareHouseSpy)

would do it.

Don Roby
  • 40,677
  • 6
  • 91
  • 113
  • 11
    This would reset all mocked methods. I think the OP wanted to reset just one method (at least that's what I came here looking for :)) – Hari Menon Dec 10 '18 at 22:02
  • Well, I'm looking to reset the method and then re-stub that method with a new value. But still keep `verify(..., times(1))` for that method stub. – tom_mai78101 Mar 30 '20 at 19:44
23

Let's say most of your tests use the stubbed response. Then you would have a setUp() method that looks like this:

@Before
public void setUp() {
  wareHouseSpy = spy(realWarehouse);
  doReturn(false).when(wareHouseSpy).isSomethingMissing();
}

Now let's say you want to undo the stubbed response and use the real implementation in one test:

@Test
public void isSomethingMissing_useRealImplementation() {
  // Setup
  when(wareHouseSpy.isSomethingMissing()).thenCallRealMethod();

  // Test - Uses real implementation
  boolean result = wareHouseSpy.isSomethingMissing();
}
Mike Tran
  • 231
  • 2
  • 3
11

It depends whether you are testing with TestNG or JUnit.

  • JUnit creates a new instance of itself for each test method. You basically don't have to worry about reseting mocks.
  • With TestNG, you have to reset the mock(s) with Mockito.reset(mockA, mockB, ...) in either an @BeforeMethod or an @AfterMethod
malana
  • 5,045
  • 3
  • 28
  • 41
bric3
  • 40,072
  • 9
  • 91
  • 111
  • Not tru about JUnit creating a new instance for every method. I have one method that is disturbing another method operation even though ALL the mocking operations are done for every method. – AgilePro Jan 30 '23 at 06:49
  • 1
    @AgilePro In 2011 there was only JUnit 4 and it's [FAQ](https://junit.org/junit4/faq.html#atests_12) mentions "_It executes each test within a separate instance of the test class_". Others wrote on the same topic : [Lifecycle of a Test Class](https://www.logicbig.com/tutorials/unit-testing/junit/lifecycle.html). And this is **also the default in JUnit 5**, see [Test instance lifecycle](https://junit.org/junit5/docs/current/user-guide/#writing-tests-test-instance-lifecycle) : "_…to be executed in isolation …, JUnit creates a new instance of each test class before executing each test method_". – bric3 Jan 31 '23 at 08:44
4

The "normal" way is to re-instantiate things in your "setUp" method. However, if you have a real object that is expensive to construct for some reason, you could do something like this:

public class MyTests {

  private static MyBigWarehouse realWarehouse = new MyBigWarehouse();
  private MyBigWarehouse warehouseSpy;

  @Before
  public void setUp() {
    warehouseSpy = spy(realWarehouse); // same real object - brand new spy!
    doReturn(false).when(wareHouseSpy).isSomethingMissing();
  }

  @Test
  ...

  @Test
  ...

  @Test
  ...
}
jhericks
  • 5,833
  • 6
  • 40
  • 60
3

Maybe I am not following but when you have a real object real:

Object mySpy = spy(real);

Then to "unspy" mySpy... just use real.

Tomasz Nurkiewicz
  • 334,321
  • 69
  • 703
  • 674
2

As per the documentation, we have

reset(mock);
//at this point the mock forgot any interactions & stubbing

The documentation specifies further

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.

Here's an example from their github repo which tests this behavior and uses it:

@Test
public void shouldRemoveAllInteractions() throws Exception {
        mock.simpleMethod(1);
        reset(mock);
        verifyZeroInteractions(mock);
}

reference : ResetTest.java

0

Addressing this piece specifically:

Is there a way, just for the sake of cleaning up after me in case some other tests that run after my tests also use the same instances and might execute a mocked method they didn't ask to mock, to un-mock a method?

If you are using JUnit, the cleanest way to do this is to use @Before and @After (other frameworks have equivalents) and recreate the instance and the spy so that no test depends on or is impacted by whatever you have done on any other test. Then you can do the test-specific configuration of the spy/mock inside of each test. If for some reason you don't want to recreate the object, you can recreate the spy. Either way, everyone starts with a fresh spy each time.

David H. Clements
  • 3,590
  • 2
  • 24
  • 26