6

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

ROMANIA_engineer
  • 54,432
  • 29
  • 203
  • 199

3 Answers3

15
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();
}
Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251
  • hello, thanks for help, after post my question i finally change the constructor to pass the good instance and its work. i just want to know if there is another solution because it was legacy code with no test and i don't want to touch the existing code, but i m agree with you, i have no choice in this case :)thanks – Mançaux Pierre-Alexandre Mar 14 '14 at 08:29
  • I think a fast refactor of legacy code is easily worth it to make testing better, but you can pursue the PowerMockito solution in the answer without changing any of the production code. You may need to adjust the PowerMockito code though; my experience with it is limited. Good luck! – Jeff Bowman Mar 14 '14 at 09:00
3

What you seem to be missing is the fact that your Spy for the Toto class is never actually being used by the Titi class.

What I would do in your case is

1) Refactor the Titi class to accept Toto as a dependency in the constructor. That way you can easily create Titi with any Toto (and there for use a mock in your unit test)

2) If option 1 is out of the question you could do the following:

public class Titi{
  private Toto toto;

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

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

  //package private - used for overriding via spying 
  Toto getToto() {
      return toto;
  }
}

@RunWith(MockitoJUnitRunner.class)
public class TitiTest {

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

   Titi testedObject = new Titi();
   testedObject = spy(testedObject);
   doReturn(toto).when(testedObject).getToto();

   testedObject.executeTiti();
 }
}
geoand
  • 60,071
  • 24
  • 172
  • 190
0

Here's an article that describes using one-line methods or factory helper methods for testing classes that don't inject collaborators. https://code.google.com/p/mockito/wiki/MockingObjectCreation