【问题标题】:Is it possible to use partial mocking for private static methods in PowerMock?是否可以在 PowerMock 中对私有静态方法使用部分模拟?
【发布时间】:2012-03-31 23:19:55
【问题描述】:

PowerMock homepage 上的示例中,我看到了以下使用 Mockito 部分模拟私有方法的示例:

@RunWith(PowerMockRunner.class)
// We prepare PartialMockClass for test because it's final or we need to mock private or static methods
@PrepareForTest(PartialMockClass.class)
public class YourTestCase {
@Test
public void privatePartialMockingWithPowerMock() {        
    PartialMockClass classUnderTest = PowerMockito.spy(new PartialMockClass());

    // use PowerMockito to set up your expectation
    PowerMockito.doReturn(value).when(classUnderTest, "methodToMock", "parameter1");

    // execute your test
    classUnderTest.execute();

    // Use PowerMockito.verify() to verify result
    PowerMockito.verifyPrivate(classUnderTest, times(2)).invoke("methodToMock", "parameter1");
}

但是,当我们希望模拟的私有方法是静态的时,这种方法似乎不起作用。我希望创建以下类的部分模拟,并模拟 readFile 方法:

package org.rich.powermockexample;

import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.List;

import static com.google.common.io.Files.readLines;

public class DataProvider {

    public static List<String> getData() {
        List<String> data = null;
        try {
            data = readFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return data;
    }

    private static List<String> readFile() throws IOException {
        File file = new File("/some/path/to/file");
        List<String> lines = readLines(file, Charset.forName("utf-8"));
        return lines;
    }

}

请有人告诉我这是如何实现的?

【问题讨论】:

  • 为什么呢?换句话说,你不能通过仅模拟 getData 来模拟什么?
  • 我可以 - 但我想看看是否可以使用 PowerMock 在部分模拟中处理私有静态方法。
  • 哦。我必须查找机制,但是AFAIK是的,通过字节码操作,就像你可以模拟或替换构造函数一样。
  • 同意 - 我认为这至少是可能的,但无法从文档中找到执行此操作的方法。

标签: java unit-testing mockito powermock


【解决方案1】:

一般来说,仅对您无法控制的类使用静态模拟(例如java.io.File)。由于DataProviderreadFile 是您自己的,因此将DataProvider 重构为适当的类(即使其方法非静态),将readFile 拉出到一个辅助对象中,然后对其进行模拟。看到这个答案https://stackoverflow.com/a/8819339/116509

【讨论】:

  • 您好,感谢您的回复。我同意这个例子有点做作,但我把它放在一起是为了探索 PowerMock 的能力,而不是解决一个特定的问题。您是说这里根本无法使用 PowerMock?
  • 我不知道,但根据我的经验,如果你在自己的类上模拟私有静态方法,你可能做错了什么。
【解决方案2】:

测试类:

@RunWith(PowerMockRunner.class)
@PrepareForTest(DataProvider.class)
public class DataProviderTest {

    @Test
    public void testGetDataWithMockedRead() throws Exception {
        mockStaticPartial(DataProvider.class, "readFile");

        Method[] methods = MemberMatcher.methods(DataProvider.class, "readFile");
        expectPrivate(DataProvider.class, methods[0]).andReturn(Arrays.asList("ohai", "kthxbye"));
        replay(DataProvider.class);

        List<String> theData = DataProvider.getData();
        assertEquals("ohai", theData.get(0));
        assertEquals("kthxbye", theData.get(1));
    }

}

正在测试的类(基本上是你的):

public class DataProvider {

    public static List<String> getData() {
        try {
            return readFile();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    private static List<String> readFile() throws IOException {
        File file = new File("/some/path/to/file");
        return readLines(file, Charset.forName("utf-8"));
    }

}

【讨论】:

  • 啊,谢谢。看起来它可以满足我的要求。我没有在 api 中找到 mockStaticPartial 方法 - 你是为此使用 powermock-mockito,还是在 easymock 分支中?
  • 从变更日志中,我看到:“* 删除了 mockStatic(Class> type, Method method, Method... methods), mockStaticPartial, mockPartial, mock(Class type, Method. .. methods) & mock(Class type, Method... methods) 来自 Mockito 扩展 API。您应该改用 PowerMockito.spy。”当我们试图模拟一个静态方法时,如果将被测类的实例提供给 spy() 方法,我看不出如何做到这一点。
  • @RichAshworth EasyMock,但可能无关紧要。
  • 好的。谢谢您的帮助。这当然适用于 EasyMock 前叉。 Thinkg 我找到了一种适用于 Mockito 版本的方法 - 将发布解决方案作为替代答案。
【解决方案3】:

经过更多研究,似乎 PowerMockito.spy() 和 PowerMockito.doReturn() 是这里需要的:

package com.richashworth.powermockexample;

import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import java.util.ArrayList;
import java.util.List;

import static org.junit.Assert.assertEquals;


@RunWith(PowerMockRunner.class)
@PrepareForTest({DataProvider.class})
public class ResultsWriterTest {

    private static List<String> mockData = new ArrayList<String>();
    private ResultsWriter resultsWriter;

    @BeforeClass
    public static void setUpOnce() {
        final String firstLine = "Line 1";
        final String secondLine = "Line 2";
        mockData.add(firstLine);
        mockData.add(secondLine);
    }

    @Before
    public void setUp() {
        resultsWriter = new ResultsWriter();
    }

    @Test
    public void testGetDataAsString() throws Exception {
        PowerMockito.spy(DataProvider.class);
        PowerMockito.doReturn(mockData).when(DataProvider.class, "readFile");

        final String expectedData = "Line 1\nLine 2\n";
        final String returnedString = resultsWriter.getDataAsString();

        assertEquals(expectedData, returnedString);
    }

}

有关更多详细信息和完整代码清单,请在此处查看我的博客文章:https://richashworth.com/post/turbocharge-your-mocking-framework-with-powermock/

【讨论】:

  • 1+:漂亮的答案,伙计。奇迹般有效。甚至 PowerMock 网站都没有像您那样描述这一点。
  • 太棒了!正是我需要的。谢谢!!
  • 对我来说也差不多了...但是如果 readFile 方法有一个参数呢?
  • 找到了,我只需要传参数,像这样:PowerMockito.doReturn(mockData).when(DataProvider.class, "readFile", param1, param2);
猜你喜欢
  • 1970-01-01
  • 2020-05-26
  • 2013-07-17
  • 1970-01-01
  • 2020-11-13
  • 2018-10-19
  • 1970-01-01
  • 1970-01-01
  • 2019-10-28
相关资源
最近更新 更多