等效的Answers.RETURNS_DEEP_STUBS为在mockito中的间谍
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了等效的Answers.RETURNS_DEEP_STUBS为在mockito中的间谍相关的知识,希望对你有一定的参考价值。
我一直无法找到一种方法来使用“Deep Stubs”来对Mockito中的间谍进行存根方法。我想要做的是这样的:
@Spy private Person person = //retrieve person
@Test
public void testStubbed() {
doReturn("Neil").when(person).getName().getFirstName();
assertEquals("Neil", person.getName().getFirstName());
}
上面的代码编译时没有任何问题,但是在运行测试时,它无法说getName()无法返回返回类型(在本例中为Name类)。
通常,在嘲笑时,你必须使用
每个模拟对象的@Mock(answer = Answers.RETURNS_DEEP_STUBS)
。然而,间谍似乎没有这样的东西。
有没有人成功地使用间谍成功进行深层嘲讽?
我收到的错误如下:
String cannot be returned by getName()
getName() should return Name
Due to the nature of the syntax above problem might occur because of:
1. Multithreaded testing
//I'm not doing multithreaded testing
2. A spy is stubbed using when(spy.foo()).then() syntax. It is safer to stub spies with doReturn|Throw() family of methods
//As shown above, I'm already using the doReturn family of methods.
虽然我仍然想知道是否有更好的方法来做到这一点,但我想为任何想要看的人发布一个解决方案。
下面的解决方案可以正常工作,要求您为每个级别的依赖项创建一个新的模拟(甚至是一个真实的对象/间谍)。换句话说,不是链接方法调用来创建存根,而是单独模拟每个级别。
@Spy private Person person = //retrieve person
@Mock private Name name;
@Test
public void testStubbed() {
doReturn(name).when(person).getName();
doReturn("Neil").when(name).getName();
assertEquals("Neil", person.getName().getFirstName());
}
你可以使用doAnswer(RETURNS_DEEP_STUBS)
更接近你想要的深存根,但是你不能覆盖任意深度的方法调用而不用去处理它们的父调用。我会像你在答案中那样坚持使用手动单级深度模拟,或者尽可能使用更少的模拟。
间谍的默认行为是委托给它真正的方法调用,它通常会返回一个真实的对象(比如你的名字)而不是一个Mockito间谍。这意味着您通常无法使用Mockito更改这些对象的行为:间谍与被侦察的对象实际上不是同一个类,而是生成的子类,其中每个字段值都从间谍中复制 - 值。 (复制是一个重要的特性,因为委派间谍会对this
有非常无意义的行为,包括方法调用和字段值。)
Foo foo = new Foo();
foo.intValue = 42;
foo.someObject= new SomeObject();
Foo fooSpy = Mockito.spy(foo);
// Now fooSpy.intValue is 42, fooSpy.someObject refers to the exact same
// SomeObject instance, and all of fooSpy's non-final methods are overridden to
// delegate to Mockito's behavior. Importantly, SomeObject is not a spy, and
// Mockito cannot override its behavior!
所以这不起作用:
doReturn("Neil").when(person).getName().getFirstName();
// Mockito thinks this call ^^^^^^^^^ should return "Neil".
这也不会:
doReturn("Neil").when(person.getName()).getFirstName();
// The object here ^^^^^^^^^^^^^^^^ won't be a mock, and even if Mockito
// could automatically make it a mock, it's not clear whether that
// should be the same spy instance every time or a new one every time.
在您的情况下,我会按照从最优先到最少的顺序选择以下内容:
- 创建一个真正的Name对象并使用
doReturn
安装它。看起来Name毕竟是一个数据对象(也就是值对象),这可能意味着它没有依赖关系,可靠的行为和难以模拟的状态转换。你可能没有通过嘲笑获得任何东西。 - 创建一个模拟名称并安装它as you do in your answer。如果Name比它看起来更复杂,或者它实际上并不存在,那么这尤其有用。
- 替换getName以返回深存根...
doAnswer(RETURNS_DEEP_STUBS).when(person).getName();
...然后你可以覆盖......doReturn("Neil").when(person.getName()).getFirstName();
......即使是任意深刻的价值观。doReturn("Gaelic").when(person.getName() .getEtymology() .getFirstNameEtymology()) .getOrigin();
作为最后的社论,部分嘲笑的一个危害是,它很难说出哪种行为是真实的,哪种行为是伪造的;这可能会让您难以保证您正在测试的行为是prod行为而不是模拟行为。深层顽固的另一个危险是你可能违反了Law of Demeter的定义。如果您发现自己经常在测试中使用这种技术,那么可能需要考虑重新架构您的被测系统。
以上是关于等效的Answers.RETURNS_DEEP_STUBS为在mockito中的间谍的主要内容,如果未能解决你的问题,请参考以下文章