432

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

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

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

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

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

4

10 回答 10

735

让我引用官方文档

监视真实物体的重要问题!

有时不可能使用 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();
于 2012-07-23T20:41:36.480 回答
37

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

于 2017-03-29T20:46:24.187 回答
28

我的情况与接受的答案不同。我试图为不在该包中的实例模拟包私有方法

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

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

声明方法protectedpublic可以解决问题,但这不是一个干净的解决方案。

于 2016-08-02T10:43:03.880 回答
20

Tomasz Nurkiewicz 的回答似乎并不能说明整个故事!

注意 Mockito 版本:1.10.19。

我是一个 Mockito 新手,所以无法解释以下行为:如果有专家可以改进这个答案,请随意。

这里讨论的方法getContentStringValue, 是NOT finalNOT static

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

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

此行调用原始方法getContentStringValue

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

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

让我们看看这里涉及的方法签名:它们static都是Matchers. Javadoc 都说两者都是 return 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 大祭司解释一下……

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

于 2016-11-06T18:46:34.427 回答
13

可能导致间谍问题的另一种可能情况是,当您正在测试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));
}

在上面的代码中,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));
}
于 2018-10-08T14:10:11.677 回答
2

监视真实物体的重要问题

当使用 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);
于 2021-11-30T14:43:43.250 回答
2

我发现 spy 调用原始方法的另一个原因。

有人想模拟一个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()工作。

于 2018-01-17T11:57:27.653 回答
0

确保不调用类中的方法的一种方法是用虚拟对象覆盖该方法。

    WebFormCreatorActivity activity = spy(new WebFormCreatorActivity(clientFactory) {//spy(new WebFormCreatorActivity(clientFactory));
            @Override
            public void select(TreeItem i) {
                log.debug("SELECT");
            };
        });
于 2018-06-19T21:28:36.753 回答
0

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

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

解决方法是创建一个实例方法或将 Mockito 升级到具有一些配置的较新版本:https ://stackoverflow.com/a/62860455/32453

于 2021-10-04T19:01:52.207 回答
-1

派对迟到了,但上述解决方案对我不起作用,所以分享我的 0.02 美元

Mokcito 版本:1.10.19

MyClass.java

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

测试.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());
于 2019-09-13T06:16:24.933 回答