在Mockito 2.2中,我使用什么来代替白盒来设置字段?

lbsnaicq  于 2022-11-08  发布在  其他
关注(0)|答案(6)|浏览(199)

在使用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);
}

我真的很喜欢这种方法,但是现在当我尝试升级到Mockito2.2.7时,我注意到(或者更确切地说,我的IDE注意到并告诉我很多次)在Mockito中不再能找到Whitebox。
我已经找到了一个替代方案,可以作为一个替代品,那就是org.powermock.reflect.Whitebox,问题是我得到了另一个依赖项(Powermock),只是为了使用Whitebox。
Powermock也有一个名为Whitebox的类,但不幸的是,它看起来似乎不能与Mockito 2.2.x一起使用
既然Whitebox不再可用,在Mockito中是否有什么好的替代方法可以用来手动“注入”字段?

溶液

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

注意@bcody建议的解决方案是一个更好的选择,如果你正在使用Spring,它不需要你维护额外的代码。

ac1kyiln

ac1kyiln1#

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

2skhul33

2skhul332#

请注意,Whitebox始终在org.mockito.internal包中。除了主版本号的递增之外,internal的指定是一个赠品,表明该包可能会受到破坏性更改的影响。
如果您确实希望在测试中设置不可访问的字段,您可以采用与setInternalState相同的方法来完成,即识别层次结构中的字段,在其上调用setAccessible,然后设置它。完整的代码在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)并不一定足够细到能够表达您试图表达的保护程度,并且通常更容易的是添加一个文档完备的初始化方法或构造函数覆盖来覆盖依赖关系,就像您试图反射性地进行的那样。您甚至可以经常将字段或方法/构造函数设置为包私有,这也是设置并行源文件夹srctests(等等)的一个很好的理由,它们代表同一个Java包的两个部分。

虽然有些人把这个额外的方法或构造函数看作是“API污染”,但我认为它是为了满足类最重要的消费者之一--* 它自己的测试 *--的需求而编写的代码。如果您需要一个原始的外部接口,您可以很容易地单独定义一个接口,这样就可以隐藏任何您想要的细节。然而,您可能会发现您 * 喜欢 * 将任何真实或模拟实现直接注入到现在更加灵活的组件中的能力,此时您可能希望研究依赖项注入模式或框架。

epfja78i

epfja78i3#

您可以在Mockito2.x中使用FieldSetter

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

pzfprimi4#

最干净、最简洁、最可移植的方法是使用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);
}
gpfsuwkq

gpfsuwkq5#

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

0pizxfdo

0pizxfdo6#

PowerMock有Whitebox as part of its public API。这是powermock-reflect的一部分。

相关问题