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

ema*_*ren 19 java tdd junit4 mockito 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);
}
Run Code Online (Sandbox Code Playgroud)

我真的很喜欢这种方法,但是现在我尝试升级到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,它不会为你维护额外的代码.我得到的信息很晚了:(

bco*_*ody 40

如果你使用的是Spring(特别是弹簧测试库),你可以简单地使用它ReflectionTestUtils.setField而不是Whitebox.setInternalState

  • 这将是比复制白盒代码更好的解决方案。我希望我早点知道这门课:) (2认同)

小智 9

您可以在 Mockito2.x 中使用 FieldSetter

    import org.mockito.internal.util.reflection.FieldSetter;
 FieldSetter.setField(eventHandler,eventHandler.getClass().getDeclaredField("securityService"), securityService);
Run Code Online (Sandbox Code Playgroud)

  • 就像 Whitebox 一样,这个类可以突然改变(因为它在内部包中)。但这看起来像是 mockito 中 Whitebox 的替代品。 (4认同)

Jef*_*ica 8

请注意,Whitebox始终在org.mockito.internal包中.除了主要版本号的增加之外,该internal名称是赠品,包装可能会受到重大变化.

如果您确实希望在测试中设置其他不可访问的字段,则可以采用与此相同的方式执行此setInternalState操作,这只是识别层次结构中的字段,调用setAccessible它,然后进行设置.完整的代码在这里是grepcode.您还可以检查许多其他方法来设置测试中的不可访问状态.

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,在这种情况下,我的一般建议是停止对抗工具:Java的四个封装级别(公共,受保护,封装,私有)不一定非常精细,足以表达您试图表达的保护程度,并且添加一个记录良好的初始化方法或构造函数覆盖来覆盖依赖关系通常要容易得多,因为你正试图反思.如果你将测试放在与它测试的类相同的Java包中,你通常甚至可以使字段或方法/构造函数包为private,这也是建立并行源文件夹srctests(等)代表两个文件夹的一个很好的理由.同一个Java包的一半.

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


foo*_*foo 6

无需重新发明轮子的最干净、最整洁和最便携的方式是使用 Apache Commons 的FieldUtils. https://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);
}
Run Code Online (Sandbox Code Playgroud)