4

我遇到了 mockito.spy 方法的麻烦。

我最近来到一个“旧”项目,我的第一个任务是在其中添加模拟,并进行真正的单元测试:)

该项目有很多概念问题,但这不是重点;)

我解释我的问题:

我有一堂课

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 的东西,因为这个类有自己的测试类 TotoTest.java。

但正如您所看到的,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

4

3 回答 3

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

请记住,您在这一行中创建的 Toto 实例上的这个间谍/存根,而不是每个新创建的 Toto。所以当你打电话时:

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

构造函数new Titi()本身会创建一个不受 Mockito 影响的 Toto 的新实例,因此对 to 的调用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();
  }
}

但我最喜欢的选项是为 Titi 创建一个辅助构造函数,用于测试:

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();
}
于 2014-03-13T18:23:16.227 回答
2

您似乎缺少的是,您的 Toto 类的 Spy 从未被 Titi 类实际使用过。

在你的情况下我会做的是

1) 重构 Titi 类以接受 Toto 作为构造函数中的依赖项。这样您就可以轻松地使用任何 Toto 创建 Titi(并在单元测试中使用模拟)

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();
 }
}
于 2014-03-13T18:27:12.910 回答
0

这是一篇文章,描述了使用单行方法或工厂辅助方法来测试不注入协作者的类。https://code.google.com/p/mockito/wiki/MockingObjectCreation

于 2014-07-24T22:22:57.880 回答