Метод шпиона мокито не работает


У меня неприятности с мокито.шпионский метод.

Я недавно прибыл на "старый" проект, и моя первая задача-добавить в него mockito и сделать реальный юнит-тест:)

У проекта много проблем с концепцией, но это не главное;)

Я объясняю свою проблему:

У меня есть класс

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
  }
}

В моем тестовом классе TitiTest.java я хочу тестировать только executeTiti, я не хочу тестировать executeToto вещи, потому что этот класс имеет свой собственный тестовый класс Тототест.Ява.

Но, как вы можете видеть, Toto является экземпляром в конструкторе titi, поэтому я попробую что-то вроде этого: (Я тоже использую PowerMock в своем тесте, поэтому я использую PowerMockRunner, но это, кажется, не проблема)

@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();
 }
}

Но реальный метод всегда вызывает и ret = 5 каждый раз :(

Я что-то пропустил? Я прочитал много постов об этом на stackoverflow и попробовал все решения, но это никогда не работает, потому что я думаю, что я делаю правильные вещи.

Я использую junit4.11/powermock1.5.4/mockito1.9.5

3 2

3 ответа:

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

Имейте в виду, что это шпионы/заглушки на экземпляре Toto, который вы создаете в этой строке, а не каждый вновь созданный Toto. Поэтому, когда вы звоните:

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

Конструктор new Titi() сам создает новый экземпляр Toto, не затронутый Mockito, так что вызов this.toto.executeAction() всегда будет возвращать 5.


Поскольку вы работаете с PowerMockito, у вас есть возможность заглушить конструктор Toto :

@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();
  }
}

Но вариант, который мне больше всего нравится, - это создать вторичный конструктор для Тити, для тестирования:

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;
}

Который затем только требует, чтобы вы скорректировали свой тест следующим образом:

@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();
}

То, что вы, кажется, упускаете, - это тот факт, что ваш шпион для класса Toto никогда на самом деле не используется классом Titi.

Что бы я сделал в вашем случае, это

1) рефакторинг класса Titi для принятия Toto в качестве зависимости в конструкторе. Таким образом, Вы можете легко создать Titi с любым Toto (и там использовать макет в вашем модульном тесте)

2) Если вариант 1 исключен, вы можете сделать следующее:

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();
 }
}

Вот статья, в которой описывается использование однострочных методов или вспомогательных методов фабрики для тестирования классов, которые не вводят коллаборационистов. https://code.google.com/p/mockito/wiki/MockingObjectCreation