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

问题描述 投票:16回答:5

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

@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的类,但不幸的是它看起来好像不能与Mockito 2.2.x一起使用

Mockito有什么好的选择我可以用来手动“注入”字段,现在Whitebox不再可用吗?


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

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

java tdd mockito junit4 powermock
5个回答
6
投票

请注意,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的四个封装级别(公共,受保护,封装,私有)不一定非常精细,足以表达您试图表达的保护程度,并且添加一个记录良好的初始化方法或构造函数覆盖来覆盖依赖关系通常要容易得多,因为你正试图反思。如果你把你的测试放在与它测试的类相同的Java包中,你通常甚至可以使字段或方法/构造函数包私有,这也是设置并行源文件夹srctests(等)的一个很好的理由。表示同一Java包的两半。

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


32
投票

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


4
投票

最简洁,最新和最便携的方式,无需重新发明轮子就是使用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);
}

1
投票

您可以在Mockito2.x中使用FieldSetter

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

1
投票

fest-reflect api中,您可以找到一个易于使用的流畅API,以支持反射。这就是我用它作为Mockito的Whiltebox的替代品。

© www.soinside.com 2019 - 2024. All rights reserved.