【问题标题】:Mockito - what rules govern injection of mocks of similar Collection classes?Mockito - 什么规则管理类似 Collection 类的模拟注入?
【发布时间】:2016-12-10 19:31:43
【问题描述】:

我只是对此感到困惑(Mockito 1.10):

@Rule
public MockitoRule rule = MockitoJUnit.rule();

@Mock
private Collection<IndexableField> mockedFieldsFromRetrievedDocument;

@Spy
@InjectMocks
private IndexManager injectedSpyIM = new IndexManager();

@Test
public void numberOfLDocsShouldBePrintedOutWithEachHitLine() throws Exception{

    LOGGER.info( String.format( "# A: %d", mockedFieldsFromRetrievedDocument.hashCode() ));
    LOGGER.info( String.format( "# fFRD %s", injectedSpyIM.getFFRD() ));

很自然,IndexManager中有一个方法getFFRD,它返回私有字段

private Collection<IndexableField> fieldsFromRetrievedDocument;

IndexManager中还有另一个私有字段:

private Collection<Closeable> closeableComponents;

记录的第一行为您提供了一个有效的哈希码。
最后一行显示

#fFRD 为空

当我检查closeableComponents的值时,我发现它的哈希码确实是注入的模拟Collection的哈希码。

然后我尝试交换IndexManager 中这些字段声明的位置:没有变化。

这里的@Mock 行似乎是 1) 完全忽略了泛型类和 2) 由于我不明白的原因,优先使用 Collection&lt;Closeable&gt; 而不是另一个...

稍后

哇,疯狂的东西:我刚刚将字段名称 closeableComponents 更改为 xcloseableComponents。现在模拟的字段确实在做我想做的事,即模拟字段fieldsFromRetrievedDocument

我的临时结论自然是,Mockito 使用它找到的第一个类型为Collection&lt;anything&gt; 的字段名称......按字母顺序排列!大概相同的选择过程适用于存在多个“相同”类型的字段的其他情况。只是在谷歌上搜索但没有成功:有人知道这是否记录在某处吗?

稍后

按照 Jeff Bowman 的建议,我更改了如下内容:

@Mock(name="fieldsFromRetrievedDocument")
private Collection<?> mockedFieldsFromRetrievedDocument;

...这是类中字段的准确拼写,大小写正确。但它仍然注入了错误的Collection&lt;?&gt; 作为模拟。那么……

我从 Mockito 1.10 更改为最新的 2.3.0:问题解决了!一个警示故事,name 属性在 1.10 的 Javadoc API 中有完整的文档...!

【问题讨论】:

    标签: java dependency-injection mocking mockito


    【解决方案1】:

    @InjectMocks documentation 描述了该行为,它可能没有您希望的记录或确定性:

    属性设置器注入; mocks 将首先通过类型解析(如果无论名称如何都会发生单个类型匹配注入),然后,如果有多个相同类型的属性,则通过属性名称和模拟名称的匹配。

    注意 1: 如果你有相同类型(或相同擦除)的属性,最好用匹配的属性命名所有 @Mock 注释字段,否则 Mockito 可能会混淆并且注入不会'不会发生。

    这是有道理的,因为该字段的泛型类型被擦除了——在运行时不可读——并且因为 Java 的反射方法 getDeclaredFieldsgetDeclaredMethods 返回了 "not in any particular order"。匹配的名称是首选,其他一切都是未定义的行为,您的重命名恰好可以为您带来好处;不要指望这种行为。

    上面命名模拟的概念是指@Mock annotation上的name属性的使用。

    【讨论】:

    • 再次感谢。接下来我尝试给注入的测试类模拟与 CUT 中的实际字段相同的名称。我不认为这会起作用......我是对的!但现在我知道@Mock 注释有一个可选的“名称”属性,我有一些工作要做。您可能会认为,面对 2 个相同类型的字段,它们的实际名称可以与 CUT 的名称进行比较......因为正在使用反射,如果 "mockedFoo".containsIgnoringCase( "foo" ) 则假定 mockedFoo 是站立的对于foo,在没有任何歧义的情况下。好像没有!
    • PS 想法 #2:通过反射获得泛型:这里有一些想法:stackoverflow.com/questions/1901164/…。 Mockito 不能按照这些思路尝试一下吗?
    • @mike 请仔细阅读“无法完成”是那个问题的正确答案。您可以获得的最接近(大多数答案所指的内容)是对于class B extends A&lt;T&gt;new A&lt;T&gt;() {},您可以反射性地将A 和T 移出类型层次结构。有一种方法可以获取泛型类型信息 out of the field definition,但要记住类型变量,您会要求 Mockito 变得非常聪明,但会牺牲测试的可读性和框架的清晰度。
    • 是的,我在考虑类型层次结构(我认为)。那么这样的事情会在测试可读性和框架清晰度方面付出代价吗?我向您在此类问题上的出色理解鞠躬,并假设 Mockito 为我们提供了足够的工具来满足我们对模拟框架的合理要求......(NB 作为一个完整的新手,我记得最近阅读了关于 PowerMock 的质疑,例如,提供“太多的魔法”...)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多