Mockito:试图监视方法是调用原始方法

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

final MyClass myClassSpy = Mockito.spy(myInstance);
Mockito.when(myClassSpy.method1()).thenReturn(myResults);

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

MyClass是一个接口,而myInstance是该接口的实现,如果这很重要的话。

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

323029 次浏览

让我引用官方文件:

重要的是,你在监视真实的物体!

有时候不可能使用when(Object)来攻击间谍。例子:

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

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

doReturn(resultsIWant).when(myClassSpy).method1();

我的情况与公认的答案不同。我试图模拟一个包私有方法的实例,该实例不在该包中

package common;


public class Animal {
void packageProtected();
}


package instances;


class Dog extends Animal { }

测试类

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

编译是正确的,但是当它试图设置测试时,它反而调用了真正的方法。

声明方法受保护的公共可以修复这个问题,尽管这不是一个干净的解决方案。

Tomasz Nurkiewicz的回答似乎并没有说出全部的故事!

NB模仿版本:1.10.19。

我是一个非常多的Mockito新手,所以不能解释以下行为:如果有一个专家在那里谁可以改善这个答案,请随意。

这里讨论的方法getContentStringValue final static

这一行调用了原来的方法getContentStringValue:

doReturn( "dummy" ).when( im ).getContentStringValue( anyInt(), isA( ScoreDoc.class ));

这一行调用了原来的方法getContentStringValue:

doReturn( "dummy" ).when( im ).getContentStringValue( anyInt(), any( ScoreDoc.class ));

出于我无法回答的原因,使用isA()会导致预期的(?)doReturn的“不调用方法”行为失败。

让我们看看这里涉及的方法签名:它们都是Matchersstatic方法。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)

API文档对此没有给出任何线索。它似乎还说,对这种“不调用方法”行为的需求“非常罕见”。就我个人而言,我使用这种技巧一直以来:通常我发现嘲笑包括几句“设置场景”……接着调用一个方法,然后在模拟上下文中“播放”你已经上演的场景…当你在设置布景和道具时,你最不想看到的就是演员进入舞台左侧,开始全身心地表演……

但这远远超出了我的职权范围…我请路过的Mockito大祭司解释一下…

*“通用参数”是正确的术语吗?

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

我找到了间谍调用原始方法的另一个原因。

有人想要模拟一个final类,并发现关于MockMaker:

因为这与我们当前的机制不同,而且有不同的局限性,因为我们想要收集经验和用户反馈,所以这个功能必须被明确激活。它可以通过mockito扩展机制来完成,创建文件src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker,其中包含一行:mock-maker-inline

来源:https://github.com/mockito/mockito/wiki/What%27s-new-in-Mockito-2#mock-the-unmockable-opt-in-mocking-of-final-classesmethods

在我合并并将该文件带到我的机器后,测试失败了。

我只需要删除该行(或文件),而spy()工作了。

确保不调用类中的方法的一种方法是使用dummy重写该方法。

    WebFormCreatorActivity activity = spy(new WebFormCreatorActivity(clientFactory) {//spy(new WebFormCreatorActivity(clientFactory));
@Override
public void select(TreeItem i) {
log.debug("SELECT");
};
});

另一个可能导致间谍问题的场景是当你测试spring bean(使用spring测试框架)或其他一些在测试期间探查对象的框架时。

例子

@Autowired
private MonitoringDocumentsRepository repository


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

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

repository = MonitoringDocumentsRepository$$EnhancerBySpringCGLIB$$MockitoMock$

@SpyBean来拯救

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

repository = MonitoringDocumentsRepository$$MockitoMock$$EnhancerBySpringCGLIB$

下面是代码:

@SpyBean
private MonitoringDocumentsRepository repository


void test(){
Mockito.doReturn(docs1, docs2)
.when(repository).findMonitoringDocuments(Mockito.nullable(MonitoringDocumentSearchRequest.class));
}

有点晚了,但以上的解决方案并不适合我,所以分享我的0.02美元

Mokcito版本:1.10.19

MyClass.java

private int handleAction(List<String> argList, String action)

Test.java

MyClass spy = PowerMockito.spy(new MyClass());

以下没有为我工作(实际的方法正在被调用):

1.

doReturn(0).when(spy , "handleAction", ListUtils.EMPTY_LIST, new String());

2.

doReturn(0).when(spy , "handleAction", any(), anyString());

3.

doReturn(0).when(spy , "handleAction", null, null);

以下工作:

doReturn(0).when(spy , "handleAction", any(List.class), anyString());

正如在一些评论中提到的,我的方法是“静态”。(尽管被类的实例调用)

public class A {
static void myMethod() {...}
}
A instance = spy(new A());
verify(instance).myMethod(); // still calls the original method because it's static

解决方法是创建一个实例方法或使用一些config: https://stackoverflow.com/a/62860455/32453将Mockito升级到一个新版本

你侦察真实物体的能力很重要

当使用间谍存根一个方法时,请使用doReturn ()方法族。

当(对象)将导致调用可以抛出异常的实际方法。

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