Mockito:试图监视方法正在调用原始方法
Posted
技术标签:
【中文标题】Mockito:试图监视方法正在调用原始方法【英文标题】:Mockito: Trying to spy on method is calling the original method 【发布时间】:2012-07-22 03:15:33 【问题描述】:我正在使用 Mockito 1.9.0。我想在 JUnit 测试中模拟一个类的单个方法的行为,所以我有
final MyClass myClassSpy = Mockito.spy(myInstance);
Mockito.when(myClassSpy.method1()).thenReturn(myResults);
问题是,在第二行中,myClassSpy.method1()
实际上被调用,导致异常。我使用模拟的唯一原因是以后,每当调用 myClassSpy.method1()
时,将不会调用真正的方法并且将返回 myResults
对象。
MyClass
是一个接口,myInstance
是一个实现,如果这很重要的话。
我需要做些什么来纠正这种间谍行为?
【问题讨论】:
看看这个:***.com/a/29394497/355438 【参考方案1】:让我引用the official documentation:
关于监视真实物体的重要问题!
有时不可能使用 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();
【讨论】:
如果我使用这种方法而我原来的方法仍然被调用怎么办?我传递的参数会不会有问题?这是整个测试:pastebin.com/ZieY790Psend
方法正在被调用
@EvgeniPetrov 如果您的原始方法仍在被调用,那可能是因为您的原始方法是最终方法。 Mockito 不模拟 final 方法,也不能警告你关于 final 方法的模拟。
是的,不幸的是静态方法是不可模拟的和“不可窥探的”。我处理静态方法的方法是在静态调用周围包装一个方法,并在该方法上使用 doNothing 或 doReturn。使用单例或 scala 对象,我将逻辑的核心移至抽象类,这使我能够拥有可以创建间谍的对象的替代测试类 impl。
如果 NOT final 和 NOT static 方法仍然被调用怎么办?
对于所有已经到了这一点的人,尝试了一切,Mokito 仍然调用原始方法 - 请在下面查看 @ejaenv 答案。【参考方案2】:
在我的例子中,使用 Mockito 2.0,我必须将所有 any()
参数更改为 nullable()
以存根真正的调用。
【讨论】:
不要让 321 个投票的最佳答案让您失望,这解决了我的问题 :) 我已经为此苦苦挣扎了几个小时! 这就是我的答案。为了让那些在模拟您的方法时更容易遵循的语法是:foo = Mockito.spy(foo);
Mockito.doReturn(someValue).when(foo).methodToPrevent(nullable(ArgumentType.class));
使用 Mockito 2.23.4 我可以确认这不是必需的,它适用于 any
和 eq
匹配器。
在 2.23.4 lib 版本上尝试了三种不同的方法:any()、eq() 和 nullable()。只有后来的工作
我正在使用 mockito-core 版本 3.4.0 并使其 nullable()
工作。有人可以解释为什么any()
不起作用吗?【参考方案3】:
我的情况与接受的答案不同。我试图模拟一个不在该包中的实例的包私有方法
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()
编译是正确的,但是当它尝试设置测试时,它会调用真实的方法。
声明方法 protected 或 public 可以解决问题,但这不是一个干净的解决方案。
【讨论】:
我遇到了类似的问题,但是 test 和 package-private 方法在同一个包中。我认为 Mockito 通常在包私有方法方面存在问题。 现在有什么已知的解决办法吗?我面临与@Maragues 类似的情况。【参考方案4】:Tomasz Nurkiewicz 的回答似乎并不能说明全部情况!
NB Mockito 版本:1.10.19。
我是一个 Mockito 新手,所以无法解释以下行为:如果有专家可以改进这个答案,请随意。
这里提到的方法getContentStringValue
是NOT final
和NOT static
。
这一行确实调用了原方法getContentStringValue
:
doReturn( "dummy" ).when( im ).getContentStringValue( anyInt(), isA( ScoreDoc.class ));
这行不调用原方法getContentStringValue
:
doReturn( "dummy" ).when( im ).getContentStringValue( anyInt(), any( ScoreDoc.class ));
由于我无法回答的原因,使用isA()
会导致doReturn
的预期(?)“不调用方法”行为失败。
我们来看看这里涉及到的方法签名:它们都是Matchers
的static
方法。 Javadoc 都说两者都返回null
,这本身就有点难以理解。可能会检查作为参数传递的Class
对象,但结果要么从未计算过,要么被丢弃。鉴于null
可以代表任何类并且您希望不调用模拟方法,isA( ... )
和any( ... )
的签名不能只返回null
而不是通用参数* @987654338 @?
无论如何:
public static <T> T isA(java.lang.Class<T> clazz)
public static <T> T any(java.lang.Class<T> clazz)
API 文档对此没有提供任何线索。似乎也说需要这种“不调用方法”行为是“非常罕见的”。我个人一直使用这种技术:通常我发现模拟涉及“设置场景”的几行......然后调用一个方法,然后在模拟中“播放”场景你已经上演的背景......当你设置场景和道具时,你最不想要的就是让演员从左边进入舞台并开始表演他们的心......
但这远远超出了我的工资等级...我请任何路过的 Mockito 大祭司解释...
* 是“通用参数”的正确术语吗?
【讨论】:
我不知道这是否会增加清晰度或进一步混淆问题,但 isA() 和 any() 之间的区别在于 isA 实际上进行类型检查,而 any() 系列方法是创建只是为了避免参数的类型转换。 @KevinWelker 谢谢。事实上,方法名称并不缺乏某种不言自明的质量。然而,我对天才的 Mockito 设计师提出质疑,尽管如此温和,因为他们没有充分记录。毫无疑问,我还需要阅读另一本关于 Mockito 的书。 PS实际上似乎很少有资源可以教“中级Mockito”! 历史是 anyXX 方法最初是作为处理类型转换的一种方式创建的。然后当有人建议他们添加参数检查时,他们不想破坏现有 API 的用户,所以他们创建了 isA() 系列。知道 any() 方法应该一直进行类型检查,他们推迟了更改这些,直到他们在 Mockito 2.X 大修中引入了其他重大更改(我还没有尝试过)。在 2.x+ 中,anyX() 方法是 isA() 方法的别名。 谢谢。对于我们这些同时进行多个库更新的人来说,这是一个关键的答案,因为过去运行的代码会突然而无声地失败。【参考方案5】:另一种可能导致间谍问题的情况是,当您正在测试 spring beans(使用 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));
【讨论】:
@SpyBean
仅在 Spring Boot 中可用:Spring 是否有类似的解决方法?【参考方案6】:
我又找到了 spy 调用原始方法的另一个原因。
有人想mock一个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()
就可以了。
【讨论】:
这就是我的原因,我试图模拟一个最终方法,但它一直在调用真实的方法,而没有明确的错误消息令人困惑。【参考方案7】:监视真实物体的重要注意事项
当使用间谍 stub 方法时,请使用 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);
【讨论】:
【参考方案8】:确保不调用类中的方法的一种方法是使用虚拟对象覆盖该方法。
WebFormCreatorActivity activity = spy(new WebFormCreatorActivity(clientFactory) //spy(new WebFormCreatorActivity(clientFactory));
@Override
public void select(TreeItem i)
log.debug("SELECT");
;
);
【讨论】:
【参考方案9】:正如在一些 cmets 中提到的,我的方法是“静态的”(尽管被类的实例调用)
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://***.com/a/62860455/32453
【讨论】:
【参考方案10】:聚会晚了,但上述解决方案对我不起作用,所以分享我的 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());
【讨论】:
以上是关于Mockito:试图监视方法正在调用原始方法的主要内容,如果未能解决你的问题,请参考以下文章