Mockito:试图窥探方法是调用原始方法

Dav*_*ave 313 java junit mockito

我正在使用Mockito 1.9.0.我想在JUnit测试中模拟一个类的单个方法的行为,所以我有

final MyClass myClassSpy = Mockito.spy(myInstance);
Mockito.when(myClassSpy.method1()).thenReturn(myResults);
Run Code Online (Sandbox Code Playgroud)

问题是,在第二行,myClassSpy.method1()实际上是被调用,导致异常.我使用模拟的唯一原因是,以后,无论什么时候myClassSpy.method1()被调用,都不会调用真正的方法并myResults返回对象.

MyClass是一个接口myInstance,如果重要的话就是它的实现.

我需要做些什么来纠正这种间谍行为?

Tom*_*icz 547

让我引用官方文件:

间谍真实物体的重要问题!

有时不可能在(对象)时使用咒语间谍.例:

List list = new LinkedList();
List spy = spy(list);

// Impossible: real method is called so spy.get(0) throws IndexOutOfBoundsException (the list is yet empty)
when(spy.get(0)).thenReturn("foo");

// You have to use doReturn() for stubbing
doReturn("foo").when(spy).get(0);
Run Code Online (Sandbox Code Playgroud)

在你的情况下它是这样的:

doReturn(resulstIWant).when(myClassSpy).method1();
Run Code Online (Sandbox Code Playgroud)

  • 如果我使用这种方法并且我的原始方法仍然被调用怎么办?我传递的参数有问题吗?这是整个测试:http://pastebin.com/ZieY790P`send`方法被调用 (23认同)
  • @EvgeniPetrov如果你的原始方法仍然被调用它可能是因为你的原始方法是最终的.Mockito并没有模拟最终方法,也无法警告你最终方法的嘲弄. (21认同)
  • 如果NOT final和NOT static方法仍然被调用怎么办? (18认同)
  • 对于每个到达这一点的人来说,尝试了所有方法,Mokito 仍然调用原始方法 - 请参阅下面的 @ejaenv 答案。 (3认同)
  • 是的,不幸的是静态方法是不可模拟的并且“不可监视”。我处理静态方法的方法是将方法包装在静态调用周围,并在该方法上使用 doNothing 或 doReturn。使用单例或 scala 对象,我将逻辑的核心移至抽象类,这使我能够拥有可以创建监视对象的备用测试类实现。 (2认同)

Mar*_*ues 25

我的案子与接受的答案不同.我试图为一个不在该包中的实例模拟一个包私有方法

package common;

public class Animal {
  void packageProtected();
}

package instances;

class Dog extends Animal { }
Run Code Online (Sandbox Code Playgroud)

和测试类

package common;

public abstract class AnimalTest<T extends Animal> {
  @Before
  setup(){
    doNothing().when(getInstance()).packageProtected();
  }

  abstract T getInstance();
}

package instances;

class DogTest extends AnimalTest<Dog> {
  Dog getInstance(){
    return spy(new Dog());
  }

  @Test
  public void myTest(){}
}
Run Code Online (Sandbox Code Playgroud)

编译是正确的,但是当它尝试设置测试时,它会调用实际方法.

声明方法受到保护公开修复问题,这不是一个干净的解决方案.

  • 我遇到了类似的问题,但 test 和 package-private 方法在同一个包中。我认为 Mockito 总体上可能存在包私有方法的问题。 (2认同)

eja*_*env 16

在我的例子中,使用Mockito 2.0,我必须将所有any()参数更改为nullable()以便存根真实的调用.

  • 不要让 321 票最高的答案让你失望,这解决了我的问题:) 我已经为此苦苦挣扎了几个小时! (5认同)
  • 这就是我的答案。为了让那些模仿你的方法的人更容易,语法是:`foo = Mockito.spy(foo);` `Mockito.doReturn(someValue).when(foo).methodToPrevent(nullable(ArgumentType.class)) ;` (5认同)
  • 我使用mockito-core版本3.4.0并使其“nullable()”有效。有人可以解释为什么“any()”不起作用吗? (4认同)
  • 但在这篇文章中,没有参数 (4认同)
  • 在 2.23.4 lib 版本上尝试了三种不同的方法:any()、eq() 和 nullable()。只有后来的工作 (3认同)
  • 没有参数的情况有什么解决办法吗?它对我来说仍然不起作用。我的方法不是最终的也不是静态的 (2认同)

mik*_*ent 15

Tomasz Nurkiewicz的回答似乎并不是讲述整个故事!

NB Mockito版本:1.10.19.

我非常喜欢Mockito newb,所以无法解释以下行为:如果有专家可以改进这个答案,请随意.

在此问题的方法,getContentStringValue不是 finalNOT static.

这一行确实调用了原始方法getContentStringValue:

doReturn( "dummy" ).when( im ).getContentStringValue( anyInt(), isA( ScoreDoc.class ));
Run Code Online (Sandbox Code Playgroud)

此行不会调用原始方法getContentStringValue:

doReturn( "dummy" ).when( im ).getContentStringValue( anyInt(), any( ScoreDoc.class ));
Run Code Online (Sandbox Code Playgroud)

由于我无法回答的isA()原因,使用导致doReturn失败的预期(?)"不调用方法"行为.

让我们看看这里涉及的方法签名:它们都是static方法Matchers.Javadoc都说要回归null,这本身就有点难以理解.据推测,Class检查作为参数传递的对象,但结果要么从未计算过,要么丢弃.鉴于它null可以代表任何类,并且你希望不被调用的模拟方法,不能签名isA( ... )any( ... )只返回null而不是通用参数*<T>

无论如何:

public static <T> T isA(java.lang.Class<T> clazz)

public static <T> T any(java.lang.Class<T> clazz)
Run Code Online (Sandbox Code Playgroud)

API文档没有提供任何关于此的线索.它似乎也说这种"不要呼叫方法"行为的需要"非常罕见".我个人使用这种技术的所有时间:通常我发现嘲讽涉及的几行其中"设置场景" ......随后调用,然后将你所上演的模拟情境"中扮演了"现场的方法..当你设置风景和道具时,你想要的最后一件事就是演员们进入舞台左边并开始表现出他们的心......

但这超出了我的工资等级...我邀请任何通过Mockito高级牧师的解释......

*是"通用参数"的正确用语吗?


Rak*_*oni 14

监视真实物体的重要问题

使用 spies 存根方法时,请使用doReturn()系列方法。

when(Object)将导致调用可能引发异常的实际方法。

List spy = spy(new LinkedList());

//Incorrect , spy.get() will throw IndexOutOfBoundsException   
 when(spy.get(0)).thenReturn("foo");

//You have to use doReturn() for stubbing    
doReturn("foo").when(spy).get(0);
Run Code Online (Sandbox Code Playgroud)


小智 12

另一种可能导致 spies 出现问题的情况是,当您测试spring bean(使用 spring 测试框架)或其他一些在 test 期间代理对象的框架时

例子

@Autowired
private MonitoringDocumentsRepository repository

void test(){
    repository = Mockito.spy(repository)
    Mockito.doReturn(docs1, docs2)
            .when(repository).findMonitoringDocuments(Mockito.nullable(MonitoringDocumentSearchRequest.class));
}
Run Code Online (Sandbox Code Playgroud)

在上面的代码中,Spring 和 Mockito 都会尝试代理您的 MonitoringDocumentsRepository 对象,但 Spring 将是第一个,这将导致真正调用 findMonitoringDocuments 方法。如果我们在将间谍放在存储库对象上之后立即调试我们的代码,它将在调试器中看起来像这样:

repository = MonitoringDocumentsRepository$$EnhancerBySpringCGLIB$$MockitoMock$
Run Code Online (Sandbox Code Playgroud)

@SpyBean 来救援

如果@Autowired我们使用@SpyBean注解来代替注解,我们将解决上面的问题,SpyBean 注解也会注入存储库对象,但它会首先被 Mockito 代理,并且在调试器中看起来像这样

repository = MonitoringDocumentsRepository$$MockitoMock$$EnhancerBySpringCGLIB$
Run Code Online (Sandbox Code Playgroud)

这是代码:

@SpyBean
private MonitoringDocumentsRepository repository

void test(){
    Mockito.doReturn(docs1, docs2)
            .when(repository).findMonitoringDocuments(Mockito.nullable(MonitoringDocumentSearchRequest.class));
}
Run Code Online (Sandbox Code Playgroud)