简体   繁体   English

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

[英]Mockito: Trying to spy on method is calling the original method

I'm using Mockito 1.9.0.我正在使用 Mockito 1.9.0。 I want mock the behaviour for a single method of a class in a JUnit test, so I have我想在 JUnit 测试中模拟一个类的单个方法的行为,所以我有

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

The problem is, in the second line, myClassSpy.method1() is actually getting called, resulting in an exception.问题是,在第二行中,实际上调用了myClassSpy.method1() ,从而导致异常。 The only reason I'm using mocks is so that later, whenever myClassSpy.method1() is called, the real method won't be called and the myResults object will be returned.我使用模拟的唯一原因是以后,无论何时myClassSpy.method1() ,都不会调用真正的方法,并且将返回myResults对象。

MyClass is an interface and myInstance is an implementation of that, if that matters. MyClass是一个接口,而myInstance是它的一个实现,如果这很重要的话。

What do I need to do to correct this spying behaviour?我需要做什么来纠正这种间谍行为?

Let me quote the official documentation :让我引用官方文档

Important gotcha on spying real objects!监视真实物体的重要问题!

Sometimes it's impossible to use when(Object) for stubbing spies.有时不可能将 when(Object) 用于 stubbing 间谍。 Example:例子:

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

In your case it goes something like:在您的情况下,它类似于:

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

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

My case was different from the accepted answer.我的情况与接受的答案不同。 I was trying to mock a package-private method for an instance that did not live in that package我试图为不在该包中的实例模拟包私有方法

package common;

public class Animal {
  void packageProtected();
}

package instances;

class Dog extends Animal { }

and the test classes和测试类

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

The compilation is correct, but when it tries to setup the test, it invokes the real method instead.编译是正确的,但是当它尝试设置测试时,它会调用真正的方法。

Declaring the method protected or public fixes the issue, tho it's not a clean solution.声明方法protectedpublic 可以解决问题,但这不是一个干净的解决方案。

The answer by Tomasz Nurkiewicz appears not to tell the whole story! Tomasz Nurkiewicz 的回答似乎并没有说明全部!

NB Mockito version: 1.10.19. NB Mockito 版本:1.10.19。

I am very much a Mockito newb, so can't explain the following behaviour: if there's an expert out there who can improve this answer, please feel free.我非常喜欢 Mockito 新手,所以无法解释以下行为:如果有专家可以改进这个答案,请随意。

The method in question here, getContentStringValue , is NOT final and NOT static .有问题的方法在这里, getContentStringValue ,是不是final不是static

This line does call the original method getContentStringValue :这一行确实调用了原始方法getContentStringValue

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

This line does not call the original method getContentStringValue :这一行没有调用原始方法getContentStringValue

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

For reasons which I can't answer, using isA() causes the intended (?) "do not call method" behaviour of doReturn to fail.由于我无法回答的原因,使用isA()会导致doReturn的预期(?)“不调用方法”行为失败。

Let's look at the method signatures involved here: they are both static methods of Matchers .我们来看看这里涉及的方法签名:它们都是Matchers static方法。 Both are said by the Javadoc to return null , which is a little difficult to get your head around in itself. Javadoc 说两者都返回null ,这本身有点难以理解。 Presumably the Class object passed as the parameter is examined but the result either never calculated or discarded.据推测,作为参数传递的Class对象被检查,但结果要么从未计算过,要么被丢弃。 Given that null can stand for any class and that you are hoping for the mocked method not to be called, couldn't the signatures of isA( ... ) and any( ... ) just return null rather than a generic parameter* <T> ?鉴于null可以代表任何类,并且您希望模拟方法不被调用, isA( ... )any( ... )的签名不能只返回null而不是泛型参数* <T> ?

Anyway:反正:

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

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

The API documentation does not give any clue about this. API 文档对此没有提供任何线索。 It also seems to say the need for such "do not call method" behaviour is "very rare".似乎也说需要这种“不调用方法”行为是“非常罕见的”。 Personally I use this technique all the time : typically I find that mocking involves a few lines which "set the scene" ... followed by calling a method which then "plays out" the scene in the mock context which you have staged... and while you are setting up the scenery and the props the last thing you want is for the actors to enter stage left and start acting their hearts out...我个人使用这种技术的所有时间:通常我发现嘲讽涉及的几行其中“设置场景” ......随后调用,然后将你所上演的模拟情境“中扮演了”现场的方法.. . . 当您设置布景和道具时,您最不希望演员进入舞台左侧并开始表演他们的心...

But this is way beyond my pay grade... I invite explanations from any passing Mockito high priests...但这远远超出了我的工资等级......我请任何路过的 Mockito 大祭司解释......

* is "generic parameter" the right term? *“通用参数”是正确的术语吗?

One more possible scenario which may causing issues with spies is when you're testing spring beans (with spring test framework) or some other framework that is proxing your objects during test .另一种可能导致 spies 出现问题的情况是,当您测试spring bean (使用 spring 测试框架)或其他一些在 test 期间代理对象的框架时

Example例子

@Autowired
private MonitoringDocumentsRepository repository

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

In above code both Spring and Mockito will try to proxy your MonitoringDocumentsRepository object, but Spring will be first, which will cause real call of findMonitoringDocuments method.在上面的代码中,Spring 和 Mockito 都会尝试代理您的 MonitoringDocumentsRepository 对象,但 Spring 将是第一个,这将导致真正调用 findMonitoringDocuments 方法。 If we debug our code just after putting a spy on repository object it will look like this inside debugger:如果我们在将间谍放在存储库对象上之后立即调试我们的代码,它将在调试器中看起来像这样:

repository = MonitoringDocumentsRepository$$EnhancerBySpringCGLIB$$MockitoMock$

@SpyBean to the rescue @SpyBean 来救援

If instead @Autowired annotation we use @SpyBean annotation, we will solve above problem, the SpyBean annotation will also inject repository object but it will be firstly proxied by Mockito and will look like this inside debugger如果我们使用@SpyBean批注代替@Autowired批注,我们将解决上述问题,SpyBean 批注也将注入存储库对象,但它将首先由 Mockito 代理,并且在调试器中看起来像这样

repository = MonitoringDocumentsRepository$$MockitoMock$$EnhancerBySpringCGLIB$

and here is the code:这是代码:

@SpyBean
private MonitoringDocumentsRepository repository

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

I've found yet another reason for spy to call the original method.我发现 spy 调用原始方法的另一个原因。

Someone had the idea to mock a final class, and found about MockMaker :有人想模拟final堂课,并发现了MockMaker

As this works differently to our current mechanism and this one has different limitations and as we want to gather experience and user feedback, this feature had to be explicitly activated to be available ;由于这与我们当前的机制不同,并且这个机制有不同的限制,并且我们想要收集经验和用户反馈,因此必须明确激活此功能才能使用; it can be done via the mockito extension mechanism by creating the file src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker containing a single line: mock-maker-inline它可以通过 mockito 扩展机制通过创建文件src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker其中包含一行: mock-maker-inline

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

After I merged and brought that file to my machine, my tests failed.在我合并并将该文件带到我的机器后,我的测试失败了。

I just had to remove the line (or the file), and spy() worked.我只需要删除该行(或文件),然后spy()工作了。

One way to make sure a method from a class is not called is to override the method with a dummy.确保类中的方法不被调用的一种方法是使用虚拟方法覆盖该方法。

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

Answer for scala users: Even putting doReturn first doesn't work! 给scala用户的答案:即使将doReturn放在首位也不起作用! See this post . 看到这篇文章

As mentioned in some of the comments, my method was "static" (though being called on by an instance of the class)正如一些评论中提到的,我的方法是“静态的”(尽管被类的实例调用)

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

Work around was make an instance method or upgrade Mockito to a newer version with some config: https://stackoverflow.com/a/62860455/32453解决方法是创建一个实例方法或使用一些配置将 Mockito 升级到更新版本: https ://stackoverflow.com/a/62860455/32453

Important gotcha on spying real objects监视真实物体的重要问题

When stubbing a method using spies , please use doReturn() family of methods.当使用 spies 存根方法时,请使用doReturn()系列方法。

when(Object) would result in calling the actual method that can throw exceptions. 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);

Bit late to the party but above solutions did not work for me , so sharing my 0.02$聚会有点晚,但以上解决方案对我不起作用,所以分享我的 0.02$

Mokcito version: 1.10.19 Mokcito 版本:1.10.19

MyClass.java我的类

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

Test.java测试.java

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

Following did NOT work for me (actual method was being called):以下对我不起作用(正在调用实际方法):

1. 1.

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

2. 2.

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

3. 3.

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

Following WORKED:以下工作:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM