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

时间:2021-03-04 05:09:54

I'm using Mockito 1.9.0. I want mock the behaviour for a single method of a class in a JUnit test, so I have

我用5 1.9.0。我希望在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. 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.

问题是,在第二行中,myclasssp .method1()实际上正在被调用,导致一个异常。我使用mock的唯一原因是,以后每当调用myclasssp .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?

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

5 个解决方案

#1


389  

Let me quote the official documentation:

让我引用官方文件:

Important gotcha on spying real objects!

Sometimes it's impossible to use when(Object) for stubbing spies. 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(resulstIWant).when(myClassSpy).method1();

#2


20  

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.

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

#3


9  

The answer by Tomasz Nurkiewicz appears not to tell the whole story!

Tomasz Nurkiewicz的回答似乎并不能说明一切!

NB Mockito version: 1.10.19.

注5版本: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 newb,所以不能解释以下行为:如果有一个专家可以改进这个答案,请放心。

The method in question here, getContentStringValue, is NOT final and NOT static.

这里的方法getContentStringValue不是final的,也不是静态的。

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()会导致预期的(?)“不叫方法”的多伦行为失败。

Let's look at the method signatures involved here: they are both static methods of Matchers. Both are said by the Javadoc to return null, which is a little difficult to get your head around in itself. Presumably the Class object passed as the parameter is examined but the result either never calculated or discarded. 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>?

让我们看一下这里涉及的方法签名:它们都是对等体的静态方法。这两种方法都是由Javadoc返回null的,这有点难于理解。假定作为参数传递的类对象被检查,但是结果不会被计算或丢弃。假设null可以表示任何类,并且您希望不调用经过模拟的方法,那么isA(…)的签名就不能是这样的吗?和任何(…)只是返回null而不是泛型参数* ?

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. 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...

API文档对此没有提供任何线索。它似乎还表示,需要这种“不调用方法”的行为“非常罕见”。就我个人而言,我一直在使用这种技巧:通常我发现mock会包含一些“设置场景”的行……然后调用一个方法,然后在模拟场景中“播放”你已经上演过的场景……当你在设置布景和道具的时候,你最不想看到的是演员们进入舞台,然后开始表演他们的心……

But this is way beyond my pay grade... I invite explanations from any passing Mockito high priests...

但这远远超出了我的工资水平……我邀请任何通过Mockito的高级祭司解释……

* is "generic parameter" the right term?

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

#4


4  

In my case, using Mockito 2.0, I had to change all the any() parameters to nullable() in order to stub the real call.

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

#5


0  

I've found yet another reason for spy to call the original method.

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

Someone had the idea to mock a final class, and found about 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:模拟-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()就可以工作了。

#1


389  

Let me quote the official documentation:

让我引用官方文件:

Important gotcha on spying real objects!

Sometimes it's impossible to use when(Object) for stubbing spies. 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(resulstIWant).when(myClassSpy).method1();

#2


20  

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.

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

#3


9  

The answer by Tomasz Nurkiewicz appears not to tell the whole story!

Tomasz Nurkiewicz的回答似乎并不能说明一切!

NB Mockito version: 1.10.19.

注5版本: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 newb,所以不能解释以下行为:如果有一个专家可以改进这个答案,请放心。

The method in question here, getContentStringValue, is NOT final and NOT static.

这里的方法getContentStringValue不是final的,也不是静态的。

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()会导致预期的(?)“不叫方法”的多伦行为失败。

Let's look at the method signatures involved here: they are both static methods of Matchers. Both are said by the Javadoc to return null, which is a little difficult to get your head around in itself. Presumably the Class object passed as the parameter is examined but the result either never calculated or discarded. 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>?

让我们看一下这里涉及的方法签名:它们都是对等体的静态方法。这两种方法都是由Javadoc返回null的,这有点难于理解。假定作为参数传递的类对象被检查,但是结果不会被计算或丢弃。假设null可以表示任何类,并且您希望不调用经过模拟的方法,那么isA(…)的签名就不能是这样的吗?和任何(…)只是返回null而不是泛型参数* ?

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. 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...

API文档对此没有提供任何线索。它似乎还表示,需要这种“不调用方法”的行为“非常罕见”。就我个人而言,我一直在使用这种技巧:通常我发现mock会包含一些“设置场景”的行……然后调用一个方法,然后在模拟场景中“播放”你已经上演过的场景……当你在设置布景和道具的时候,你最不想看到的是演员们进入舞台,然后开始表演他们的心……

But this is way beyond my pay grade... I invite explanations from any passing Mockito high priests...

但这远远超出了我的工资水平……我邀请任何通过Mockito的高级祭司解释……

* is "generic parameter" the right term?

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

#4


4  

In my case, using Mockito 2.0, I had to change all the any() parameters to nullable() in order to stub the real call.

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

#5


0  

I've found yet another reason for spy to call the original method.

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

Someone had the idea to mock a final class, and found about 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:模拟-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()就可以工作了。