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

What do I use instead of Whitebox in Mockito 2.2 to set fields?

使用 Mockito 1.9.x 我一直在使用 Whitebox 将字段值设置为 "inject" 模拟。请参阅以下示例:

@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 注意到并告诉了我很多次)在 Mockito 中不再找到 Whitebox。

我找到了一个替代品,它可以作为替代品,那就是 org.powermock.reflect.Whitebox,问题是我得到了另一个依赖项 (Powermock),只是为了使用 Whitebox .

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

一起使用

现在 Whitebox 不再可用,我可以使用 Mockito 中手动 "inject" 字段的任何好的替代方法吗?


解决方案

我在评论中回复了@JeffBowman 的post。简而言之,我选择复制 WhiteBox 的代码并使用它,因为它在大多数测试用例中使用,并且 class 不依赖于其他 classes。这是解决这个问题的最快途径。

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

请注意 Whitebox 总是在 org.mockito.internal 包中。除了主要版本号的递增之外,internal 标志是一个赠品,表明软件包可能会受到重大更改。

如果您确实想在测试中设置一个点,否则无法访问的字段,您可以按照与 setInternalState 相同的方式进行操作,这只是在层次结构中标识字段, 在其上调用 setAccessible ,然后设置它。 The full code is here on grepcode. You can also examine a number of 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)不一定足够细化以表达您要表达的保护程度,而且通常添加记录良好的初始化方法或构造函数覆盖要容易得多在您尝试反思时覆盖依赖项。如果你把你的测试放在与它测试的 class 相同的 Java 包中,你通常甚至可以使字段或 method/constructor 包私有,这也是设置的一个很好的理由并行源文件夹 srctests(等)代表同一 Java 包的两半。

虽然有些人将此附加方法或构造函数视为 "API pollution",但我认为它是根据您的 class 最重要的消费者之一的要求进行编码 - its自己测试。如果您需要一个原始的外部接口,您可以轻松地单独定义一个接口,这样您就可以隐藏任何您想要的细节。然而,您可能会发现您 喜欢 将任何真实或模拟实现直接注入到现在更灵活的组件中的能力,此时您可能需要研究依赖注入模式或框架.

如果您正在使用 Spring(特别是 spring-test 库),您可以简单地使用 ReflectionTestUtils.setField 而不是 Whitebox.setInternalState

无需重新发明轮子的最干净、最整洁和最便携的方法是使用 Apache Commons 的 FieldUtilshttps://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/reflect/FieldUtils.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);
}

您可以在 Mockito2.x

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

在这里 fest-reflect api 你可以找到一个易于使用的流利 API 来支持反射。这就是我用来替代 Mockito 的 Whiltebox 的东西。