我在Mockito 2.2中用什么代替Whitebox来设置字段?

时间:2016-10-27 09:26:33

标签: java tdd mockito junit4 powermock

使用Mockito 1.9.x时,我一直使用Whitebox设置字段值以“注入”模拟。以下示例:

@Before
public void setUp() {

    eventHandler = new ProcessEventHandler();
    securityService = new SecurityServiceMock();
    registrationService = mock(RegistrationService.class);

    Whitebox.setInternalState(eventHandler, "registrationService", registrationService);
    Whitebox.setInternalState(eventHandler, "securityService", securityService);
}

我真的很喜欢这种方法,但是现在我尝试升级到Mockito 2.2.7我注意到(或者更确切地说,我的IDE注意到并告诉了我很多次)Whitebox不再是在Mockito找到。

我找到了一个可以替代的替代方案,那就是org.powermock.reflect.Whitebox,问题在于我得到另一个依赖项(Powermock),只是为了使用Whitebox。

Powermock也有一个名为Whitebox的类,但不幸的是它看起来好像不能用于Mockito 2.2.x

现在Whitebox已不再可用,我可以使用Mockito中的任何好的替代品来手动“注入”字段吗?

解决方案

我在回复@JeffBowman发表的帖子时写了一篇评论。简而言之,我选择复制WhiteBox的代码,并使用它,因为它在大多数测试用例中使用,并且该类与其他类没有依赖关系。这是解决这个问题的最快途径。

注意 @bcody建议的解决方案是更好的选择,如果您使用的是spring,它不会为您维护额外的代码。我得到的信息很晚了:(

5 个答案:

答案 0 :(得分:31)

如果您使用的是Spring(特别是弹簧测试库),您只需使用ReflectionTestUtils.setField代替Whitebox.setInternalState

答案 1 :(得分:5)

请注意,Whitebox始终位于org.mockito.internal包中。除了主要版本号的增加之外,internal指定是一个赠品,包装可能会受到重大变化。

如果您确实希望在测试中设置其他不可访问的字段,则可以采用与setInternalState相同的方式执行此操作,这只是为了识别层次结构中的字段,调用setAccessible就可以了,然后设置它。 The full code is here on grepcode.您还可以检查一些other ways to set inaccessible state in tests

public static void setInternalState(Object target, String field, Object value) {
    Class<?> c = target.getClass();
    try {
        Field f = getFieldFromHierarchy(c, field);  // Checks superclasses.
        f.setAccessible(true);
        f.set(target, value);
    } catch (Exception e) {
        throw new RuntimeException(
            "Unable to set internal state on a private field. [...]", e);
    }
}

但是,在这种情况下,我的一般建议是停止对抗工具:Java的四个封装级别(public,protected,package,private)不是必须足够精细,以表达您试图表达的保护程度,并且通常更容易添加一个记录良好的初始化方法或构造函数覆盖来覆盖依赖关系,因为您正在尝试反思。如果将测试放在与其测试的类相同的Java包中,您通常甚至可以将字段或方法/构造函数包为private,这也是设置并行源文件夹src和{的一个很好的理由。 {1}}(等)表示同一Java包的两半。

虽然有些人将这种额外的方法或构造函数视为“API污染”,但我认为它是编码满足您班级最重要的消费者的要求 - 自己的测试。如果您需要一个原始的外部界面,您可以轻松地单独定义一个,以便您可以隐藏任何您想要的细节。但是,您可能会发现喜欢能够将任何实际或模拟实现直接注入到您现在更灵活的组件中,此时您可能需要查看依赖注入模式或框架。

答案 2 :(得分:3)

最简洁,最好,最强大,最便携的方式,无需重新发明轮子就是使用Apache Commons'FieldUtilshttps://commons.apache.org/proper/commons-io/javadocs/api-2.5/org/apache/commons/io/FileUtils.html

您的问题的答案将是

public static void setStaticFieldValue(
        @NonNull final Class<?> clz,
        @NonNull final String fieldName,
        @NonNull final Object value) throws Exception {
    final Field f = FieldUtils.getField(clz, fieldName, true);
    FieldUtils.removeFinalModifier(f);
    f.set(null, value);
}

答案 3 :(得分:1)

您可以在Mockito2.x中使用FieldSetter

    import org.mockito.internal.util.reflection.FieldSetter;
 FieldSetter.setField(eventHandler,eventHandler.getClass().getDeclaredField("securityService"), securityService);

答案 4 :(得分:1)

在这里,使用fest-reflect API,您可以找到易于使用的流利API来提供反射支持。这就是我用来替代Mockito的Whiltebox的方式。