java - 如何模拟 Final 类并进行代码覆盖

标签 java junit code-coverage powermock processbuilder

我无法找到解决我的 JUnit 问题的方法,因此我尝试最大限度地简化它,以便我希望它很容易理解。

基本上,我正在尝试测试这个类:

public class PB {
    public int startProcessBuilder() {
        int status = 1;
        try {
            ProcessBuilder pb = new ProcessBuilder("java", "-jar", ".....");
            pb.directory(new File("/directory"));
            Process process = pb.start();
            status = process.waitFor();
        } catch (IOException | InterruptedException e) {
            System.out.println(e.getMessage());
        }
        return status;
    }
}

所以我想出了这个测试:

@RunWith(PowerMockRunner.class)
@PrepareForTest({ ProcessBuilder.class, PB.class })
public class PBTest {

    private PB spyInstance = Mockito.spy(PB.class);
    private ProcessBuilder processBuilderMock = PowerMockito.mock(ProcessBuilder.class);
    private Process processMock = Mockito.mock(Process.class);

    @Before
    public void initialize() throws Exception {
        PowerMockito.whenNew(ProcessBuilder.class).withParameterTypes(String[].class).withArguments(anyVararg())
                .thenReturn(processBuilderMock);
        PowerMockito.doReturn(processMock).when(processBuilderMock).start();
    }

    @Test
    public void testStartProcessBuilder() throws Exception {
        assertThat(spyInstance.startProcessBuilder(), is(0));
    }
}

我知道我的测试运行成功,但在我工作的公司中,我们使用 jacoco 和 eclemma 来显示代码覆盖率,并且如果类中所有代码都显示为 0% 覆盖率,这是一个已知问题我们正在测试的是@PrepareForTest注释。

因此,我们现在使用了一个已知的解决方案,即使用 MockitoJUnitRunner ( http://www.notonlyanecmplace.com/make-eclemma-test-coverage-work-with-powermock/ )

@RunWith(MockitoJUnitRunner.class)
@PrepareForTest({ ProcessBuilder.class, PB.class })
public class PBTest {

    private PB spyInstance = Mockito.spy(PB.class);
    private ProcessBuilder processBuilderMock = PowerMockito.mock(ProcessBuilder.class);
    private Process processMock = Mockito.mock(Process.class);

    @Rule
    public PowerMockRule rule = new PowerMockRule();

    static {
        PowerMockAgent.initializeIfNeeded();
    }

    @Before
    public void initialize() throws Exception {
        PowerMockito.whenNew(ProcessBuilder.class).withParameterTypes(String[].class).withArguments(anyVararg())
                .thenReturn(processBuilderMock);
        PowerMockito.doReturn(processMock).when(processBuilderMock).start();
    }

    @Test
    public void testStartProcessBuilder() throws Exception {
        assertThat(spyInstance.startProcessBuilder(), is(0));
    }
}

现在真正的问题来了: 当我尝试运行测试时,出现此异常: org.mockito.exceptions.misusing.NotAMockException:传递给when()的参数不是模拟!显示这一行:

PowerMockito.doReturn(processMock).when(processBuilderMock).start();

所以是的,显然 processBuilderMock 不是一个模拟而是一个 powermock,所以我尝试替换这两行

private ProcessBuilder processBuilderMock = PowerMockito.mock(ProcessBuilder.class);

PowerMockito.doReturn(processMock).when(processBuilderMock).start();

通过这个:

private ProcessBuilder processBuilderMock = Mockito.mock(ProcessBuilder.class);

PowerMockito.doReturn(processMock).when(processBuilderMock).start();

但是当然:不能模拟/监视类 java.lang.ProcessBuilder... 因为它是一个最终类(可能是我首先使用 PowerMock 的原因)

我有什么选择?

最佳答案

您可以设计PB类以易于测试。一种方法是提取 ProcessBuilder 参数:

public class PB {
  public int startProcessBuilder(String... args) {
    try {
      ProcessBuilder pb = new ProcessBuilder(args);

稍后在测试中您可以使用一个小的“Hello World”测试 JAR:

new PB().startProcessBuilder("java", "-jar", "path-to-test-jar");

或使用标准 echo 命令,无论操作系统如何,该命令都应具有相同的语法:

new PB().startProcessBuilder("echo", "Hello", "World");

您不需要模拟任何东西,您实际上可以使用模拟 JAR 来调用模拟 Java 进程。

您费了这么大的劲来提高覆盖率,这一事实凸显出您当前的开发流程是有问题的。覆盖率本身并不是一个目标,它是一个可以让您对代码充满信心的指标。如果您必须通过避免效果良好的 @PrepareForTest 来增强它,那么有什么意义呢?

关于java - 如何模拟 Final 类并进行代码覆盖,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53485165/

相关文章:

java - 如何隐藏 s3 存储桶并从 Html 文件访问 key 凭据

java - 给出 Java 项目 (Netbeans) 中文件的固定路径

java - 每个测试套件并行测试设置/拆卸一次?测试组织

android - 在 Android 上测试代码覆盖率有哪些替代方案?

java - 从类路径资源 [applicationContext.xml] 解析 XML 文档时发生意外异常;

java - 内部类的构造函数引用在运行时因 VerifyError 而失败

android - 如何测试一个 Android 库项目

java - 我将如何使用 Junit 测试这个简单的方法?

php - 是否有适用于手动测试的代码覆盖工具?

c++ - 如何在 Travis CI 中为 C++ with/CMake 项目正确配置 CodeCov?