javamockingmockitospy

Mockito spy method not working


I'm in trouble with mockito.spy method.

I'm recently arrived on a "old" project and my first task is to add mockito in it, and to do real unit test :)

the project has many conception problems but its not the point here ;)

I explain my problem:

I have a class

public class Tutu{
  public Tutu(){
  }
}

public class Toto{
  public Toto(){
  }
  public int executeToto(Tutu tutu){
    //do some stuff
    return 5;
  }
}

public class Titi{
  private Toto toto;

  public Titi(){
     this.toto = new Toto();     
  }

  public void executeTiti(){
      //do some stuff
      Tutu tutu = new Tutu();
      int ret = this.toto.executeToto(tutu);
      //do some stuff
  }
}

in my test class TitiTest.java I want to test only executeTiti, I don't want to test executeToto stuff because this class has is own test class TotoTest.java.

but as you can see, toto is instantiate in titi constructor so I try something like this: (I'm using PowerMock in my test too, so I'm using PowerMockRunner but it doesn't seem to be the problem)

@RunWith(PowerMockRunner.class)
public class TitiTest {

 @Test
 public void testExecuteTiti(){
   Toto toto = Mockito.spy(new Toto());
   Mockito.doReturn(2).when(toto).executeToto(Mockito.any(Tutu.class));

   Titi testedObject = new Titi();
   testedObject.executeTiti();
 }
}

but the real method is always calling and ret = 5 everytime :(

Do I miss something? I read many post about this on stackoverflow and try all solution but it's never work because I think I'm doing right thing.

I use junit4.11/powermock1.5.4/mockito1.9.5


Solution

  • Toto toto = Mockito.spy(new Toto());
    

    Bear in mind that this spies/stubs on the Toto instance you create in this line, and not every newly-created Toto. So when you call:

    Titi testedObject = new Titi();
    testedObject.executeTiti();
    

    The constructor new Titi() itself creates a new instance of Toto, unaffected by Mockito, so that call to this.toto.executeAction() will always return 5.


    Because you're running with PowerMockito, you do have the option of stubbing Toto's constructor:

    @RunWith(PowerMockRunner.class)
    @PrepareForTest(Titi.class) // stub the calling class Titi, not Toto!
    public class TitiTest {
      @Test public void testExecuteTiti() {
        Toto toto = Mockito.spy(new Toto());
        Mockito.doReturn(2).when(toto).executeToto(Mockito.any(Tutu.class));
    
        PowerMockito.whenNew(Toto.class).withAnyArguments().thenReturn(toto);
    
        Titi testedObject = new Titi();
        testedObject.executeTiti();
      }
    }
    

    But the option I like the best is to create a secondary constructor for Titi, for testing:

    public Titi(){
      this.toto = new Toto();     
    }
    
    /** For testing only. Uses the passed Toto instance instead of a new one. */
    Titi(Toto toto){
      this.toto = toto;
    }
    

    Which then only requires you to adjust your test like this:

    @Test public void testExecuteTiti(){
      Toto toto = Mockito.spy(new Toto());
      Mockito.doReturn(2).when(toto).executeToto(Mockito.any(Tutu.class));
    
      Titi testedObject = new Titi(toto);
      testedObject.executeTiti();
    }