Java Mockito:试图监视方法正在调用原始方法

Java Mockito:试图监视方法正在调用原始方法,java,junit,mockito,Java,Junit,Mockito,我正在使用Mockito 1.9.0。我希望在JUnit测试中模拟类的单个方法的行为,因此 final MyClass myClassSpy = Mockito.spy(myInstance); Mockito.when(myClassSpy.method1()).thenReturn(myResults); 问题是,在第二行中,myClassSpy.method1()实际上被调用,导致异常。我使用mock的唯一原因是,以后无论何时调用myClassSpy.method1(),都不会调用真正的

我正在使用Mockito 1.9.0。我希望在JUnit测试中模拟类的单个方法的行为,因此

final MyClass myClassSpy = Mockito.spy(myInstance);
Mockito.when(myClassSpy.method1()).thenReturn(myResults);
问题是,在第二行中,
myClassSpy.method1()
实际上被调用,导致异常。我使用mock的唯一原因是,以后无论何时调用
myClassSpy.method1()
,都不会调用真正的方法,并且会返回
myResults
对象

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

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

让我引用一下:

重要的是,我已经掌握了监视真实物体的方法! 有时,当(对象)用于刺杀间谍时,是不可能的。例如:

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(){}
}
包通用;
公共抽象类动物测试{
@以前
设置(){
doNothing().when(getInstance()).packageProtected();
}
抽象T getInstance();
}
包实例;
类DogTest扩展了AnimalTest{
Dog getInstance(){
返回间谍(新狗());
}
@试验
public void myTest(){}
}
编译是正确的,但是当它试图设置测试时,它会调用real方法


宣布该方法受保护或公开解决了这个问题,尽管这不是一个干净的解决方案。

托马兹·努尔基维茨的回答似乎并没有说明全部情况

NB Mockito版本:1.10.19

我是一个莫基托新手,所以无法解释以下行为:如果有专家可以改进这个答案,请放心

这里讨论的方法,
getContentStringValue
,是
最终
静态

此行调用原始方法
getContentStringValue

doReturn( "dummy" ).when( im ).getContentStringValue( anyInt(), isA( ScoreDoc.class ));
doReturn( "dummy" ).when( im ).getContentStringValue( anyInt(), any( ScoreDoc.class ));
此行不调用原始方法
getContentStringValue

doReturn( "dummy" ).when( im ).getContentStringValue( anyInt(), isA( ScoreDoc.class ));
doReturn( "dummy" ).when( im ).getContentStringValue( anyInt(), any( ScoreDoc.class ));
出于我无法回答的原因,使用
isA()
会导致
doReturn
的预期(?“不要调用方法”行为失败

让我们看看这里涉及的方法签名:它们都是
匹配器的
静态方法。Javadoc说这两个函数都返回
null
,这本身就有点难理解。假定检查作为参数传递的
对象,但结果从未计算或丢弃。鉴于
null
可以代表任何类,并且您希望模拟方法不会被调用,那么
isA(…)
any(…)
的签名不能只返回
null
而不是泛型参数*

无论如何:

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

public static <T> T any(java.lang.Class<T> clazz)
publicstatictisa(java.lang.Class clazz)
公共静态T any(java.lang.Class clazz)
API文档没有给出任何关于这方面的线索。它似乎还说,这种“不调用方法”行为的必要性“非常罕见”。就我个人而言,我一直都在使用这种技巧:我发现模仿通常包括几句“设置场景”的台词。。。然后调用一个方法,该方法将“播放”已上演的模拟上下文中的场景。。。当你在设置布景和道具的时候,你最不想看到的就是演员们从左边进入舞台,开始表演他们的心

但这远远超出了我的工资等级。。。我请任何路过的莫基托大祭司解释


*“泛型参数”是正确的术语吗?

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

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

有人想模拟一个
final
类,并找到了关于
MockMaker

由于这与我们当前的机制不同,并且这一机制有不同的限制,并且我们希望收集经验和用户反馈,因此必须明确激活此功能才能使用;可以通过mockito扩展机制创建文件
src/test/resources/mockito extensions/org.mockito.plugins.MockMaker
,其中包含一行:
mock maker inline

资料来源:

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


我只需要删除行(或文件),并且
spy()
工作。

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

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

另一种可能会导致spies出现问题的情况是,您正在测试SpringBeans(使用SpringTestFramework)或其他在测试期间代理对象的框架

范例

@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)
以下方法对我不起作用(实际的方法是
doReturn(0).when(spy , "handleAction", any(), anyString());
doReturn(0).when(spy , "handleAction", null, null);
doReturn(0).when(spy , "handleAction", any(List.class), anyString());