【问题标题】:How do I mock a private static final field initialized via a private constructor using Powermock and Mockito?如何使用 Powermock 和 Mockito 模拟通过私有构造函数初始化的私有静态最终字段?
【发布时间】:2016-09-10 17:51:54
【问题描述】:

这是我的源类 -

public class ClassToTest extends AbstractSuperClass<Integer> {
    private static final ClassToTest INSTANCE = new ClassToTest(); // (line 1) need to mock this variable

    static ClassToTest get() {
        return INSTANCE;
    }
    private ClassToTest() {
        super(Integer.class);// (line 2)
    }
}

这是我迄今为止的测试尝试

@RunWith(PowerMockRunner.class)
@PrepareForTest(ClassToTest.class)
public class TestClass {
    private ClassToTest testClass;
    @Before
    public void setUp() {
        // each of the below attempts fails at line 1  because of the call to line 2 (annotated above).
        // Attempt A.  
        testClass = WhiteBox.newInstance(ClassToTest.class);
        //Attempt B.
        testClass = mock(ClassToTest.class);
        WhiteBox.setInternalState(ClassToTest.class, "INSTANCE", testClass);
    }
    @Test
    public void dummy() {
        // irrelevant
    }
}

我正在尝试有效地模拟 ClassToTest.INSTANCE 并调用它的私有构造函数。我怎么能那样做?

编辑:从AbstractSuperClass 调用的片段/构造函数。

public abstract class AbstractSuperClass<V extends Serializable> {
    private final CacheClient<V> cache;
    private final int seconds;

    public AbstractSuperClass(Class<V> valueType) {
        cache = new ClientFactory(Config.getAppConfig(), StatisticSet.getGlobalStatistics()).newClient(getCacheType(), valueType);
        seconds = Config.getAppConfig().get(getCacheType().getSectionEnum()).getSeconds();
    }

P.S:我试图避免处理 AbstractSuperClass 的内部问题,最初希望只是简单地模拟调用。我也愿意接受任何重构ClassToTest 以避免这种情况的想法。

【问题讨论】:

  • 您模拟ClassToTest.INSTANCE 接缝工作的解决方案,如果您将testClass.doSomething(); verify(testClass).doSomething(); 添加到dummy 方法,那么测试通过了,所以我看不出真正的问题是什么(我使用尝试 B)。您能否就问题所在添加更多信息?
  • @GergelyToth - 我猜这可能与私有构造函数对 AbstractSuperClass 的调用有关,我已经编辑了问题并添加了该类的更多详细信息。
  • 我很想知道使用 Mockito/PowerMock 是否有解决此问题的方法。在我的情况下,我希望测试另一个具有 ClassToTest 作为依赖项的类,所以我基本上将 ClassToTest 提取到一个接口并提供了一个模拟实现以供测试,类似于 here的解释>

标签: java unit-testing mockito powermock white-box


【解决方案1】:

您的真正问题是您使用静态创建了难以测试的、几乎不可测试的代码。不仅如此:您还创造了一个糟糕的设计。因为您将生产类彼此紧密耦合。一旦到位,以后要摆脱该静态方法将是困难。

因此,另一种选择:与其尝试使用 PowerMock 来“修复”损坏的设计,不如退后一步。您了解“编写可测试”代码的实际意义(例如通过观看这些videos);然后你使用纯接口和依赖注入来解决你的问题。您可以使用 EasyMock 或 Mockito 测试所有这些,而无需 Powermock!

【讨论】:

  • 这个。 Powermock 是一个残忍的情妇——它会在短期内帮助你,但从长远来看会伤害你。如前所述,它安抚了糟糕的设计并消耗了您的 @RunWith,并且可能会因为它的作用而导致与其他库的棘手的兼容性问题。
  • 像往常一样,生活中的一切,以及 IT 生活中的一切都是关于平衡的。如果您正在处理第 3 方/遗留代码;你可以或不想改变;那么 Powermock 可能仍然有帮助。但非常简单的规则是:如果您正在创建新代码,那么您就不需要 Powermock。故事结束。
【解决方案2】:

我不明白你想要实现什么,但这在这里工作:

@PrepareForTest(ClassToTest.class) // required
@RunWith(PowerMockRunner.class)    // required
public class ClassToTestTest {

    private ClassToTest testClass;

    @Before
    public void setUp() throws Exception {
        this.testClass = Mockito.mock(ClassToTest.class);
        final Field instance = ClassToTest.class.getDeclaredField("INSTANCE");
        instance.setAccessible(true);
        instance.set(null, this.testClass);
    }

    @Test
    public void testGet() throws Exception {
        assertSame(this.testClass, ClassToTest.get());
        System.out.println(this.testClass);
    }
}

输出:

Mock for ClassToTest, hashCode: 1083021083

(使用 powermock-api-mockito 1.6.2 版测试)

【讨论】:

    【解决方案3】:

    我不认为模拟字段是正确的方法,我什至不相信可以这样做,因为您不能覆盖字段只有方法可以被覆盖,即实际上模拟如何做和工作。事实上,模拟只是我们覆盖方法的某种代理。

    您应该将ClassToTest.get() 模拟为下一个:

    @RunWith(PowerMockRunner.class)
    @PrepareForTest(ClassToTest.class)
    public class TestClass {
        private ClassToTest testClass;
        @Before
        public void setUp() {
            testClass = PowerMockito.mock(ClassToTest.class);
            PowerMockito.mockStatic(ClassToTest.class);
            Mockito.when(ClassToTest.get()).thenReturn(testClass);
        }
    
        @Test
        public void dummy() {
            // Here I get the instance of ClassToTest that I mocked in the setUp method
            System.out.println(ClassToTest.get());
        }
    }
    

    【讨论】:

    • 这在PowerMockito.mock(ClassToTest.class) 再次失败,分别由第 1 行和第 2 行引起,这意味着调用尚未被模拟:( .
    • 我在没有 AbstractSuperClass 的情况下复制/粘贴了您的代码,因为您没有提供它并且它可以工作
    • 然后请提供 AbstractSuperClass
    • @NicolasFilotto,我认为这可能是我们看到不同结果的区别(?)AbstractSuperClass 在我的案例中实际上加载了一堆配置,super() 可能导致失败。
    • 如果您不按照 MCVE 规则 stackoverflow.com/help/mcve 提供缺失的类,则很难提供帮助
    猜你喜欢
    • 1970-01-01
    • 2015-08-22
    • 1970-01-01
    • 1970-01-01
    • 2016-08-17
    • 2013-10-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多