提问人:Dave 提问时间:7/24/2012 最后编辑:adam_0Dave 更新时间:11/30/2021 访问量:363329
Mockito:试图监视方法就是调用原始方法
Mockito: Trying to spy on method is calling the original method
问:
我正在使用 Mockito 1.9.0。我想在 JUnit 测试中模拟类的单个方法的行为,所以我有
final MyClass myClassSpy = Mockito.spy(myInstance);
Mockito.when(myClassSpy.method1()).thenReturn(myResults);
问题是,在第二行中,实际上被调用,导致异常。我使用 mocks 的唯一原因是,以后无论何时调用,都不会调用真正的方法,而是返回对象。myClassSpy.method1()
myClassSpy.method1()
myResults
MyClass
是一个接口,并且是它的实现,如果这很重要的话。myInstance
我需要做些什么来纠正这种间谍行为?
答:
让我引用官方文档:
间谍真实物体的重要陷阱!
有时无法使用 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();
评论
send
我的情况与公认的答案不同。我试图模拟一个包私有方法,用于不存在于该包中的实例
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(){}
}
编译是正确的,但是当它尝试设置测试时,它会调用实际方法。
声明受保护或公开的方法可以解决问题,但这不是一个干净的解决方案。
评论
Tomasz Nurkiewicz 的回答似乎并不能说明全部情况!
NB Mockito 版本:1.10.19。
我非常喜欢 Mockito 新手,所以无法解释以下行为:如果有专家可以改进这个答案,请随时。
这里讨论的方法 是 NOT 和 NOT 。getContentStringValue
final
static
此行调用原始方法:getContentStringValue
doReturn( "dummy" ).when( im ).getContentStringValue( anyInt(), isA( ScoreDoc.class ));
此行不调用原始方法:getContentStringValue
doReturn( "dummy" ).when( im ).getContentStringValue( anyInt(), any( ScoreDoc.class ));
由于我无法回答的原因,使用会导致预期的 (?“不调用方法”行为失败。isA()
doReturn
我们来看一下这里涉及的方法签名:它们都是 的方法。两者都被 Javadoc 说要返回,这本身有点难以理解。据推测,作为参数传递的对象被检查,但结果要么从未计算过,要么被丢弃。鉴于它可以代表任何类,并且您希望不调用模拟方法,那么 和 的签名不能返回而不是泛型参数* 吗?static
Matchers
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 大祭司解释......
* “通用参数”是正确的术语吗?
评论
就我而言,使用 Mockito 2.0,我必须将所有参数更改为 才能存根实际调用。any()
nullable()
评论
foo = Mockito.spy(foo);
Mockito.doReturn(someValue).when(foo).methodToPrevent(nullable(ArgumentType.class));
nullable()
any()
我找到了间谍调用原始方法的另一个原因。
有人想嘲笑一堂课,并发现: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");
};
});
另一种可能导致间谍问题的情况是,当您测试 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救援
如果我们使用注解代替注解,我们将解决上述问题,SpyBean注解也将注入存储库对象,但它将首先由 Mockito 代理,并在调试器中看起来像这样@Autowired
@SpyBean
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 是否有类似的解决方法?
派对有点晚了,但上述解决方案对我不起作用,所以分享我的 0.02 美元
Mokcito 版本:1.10.19
我的类:.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);
以下 WORKED:
doReturn(0).when(spy , "handleAction", any(List.class), anyString());
正如一些评论中提到的,我的方法是“静态的”(尽管被类的实例调用)
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
关于监视真实物体的重要陷阱
当使用 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);
评论