如何对使用 ProcessBuilder 和 Process 的 Java 方法进行单元测试?

2022-09-03 14:33:01

我有一个Java方法,它使用ProcessBuilder启动一个Process,并将其输出管道化为字节数组,然后在进程完成后返回其字节数组。

伪代码:

ProcessBuilder b = new ProcessBuilder("my.exe")
Process p = b.start();
... // get output from process, close process

对这种方法进行单元测试的最佳方法是什么?我还没有找到一种方法来嘲笑ProcessBuilder(它是最终的),即使使用令人难以置信的JMockit,它也给了我一个NoClassDefFoundError:

java.lang.NoClassDefFoundError: test/MockProcessBuilder
    at java.lang.ProcessBuilder.<init>(ProcessBuilder.java)
    at mypackage.MyProcess.start(ReportReaderWrapperImpl.java:97)
    at test.MyProcessTest.testStart(ReportReaderWrapperImplTest.java:28)

有什么想法吗?


- 正如Olaf所建议的那样,我最终将这些行重构到一个接口上。

Process start(String param) throws IOException;

我现在将此接口的一个实例传递到要测试的类(在其构造函数中),通常使用具有原始行的默认实现。当我想测试时,我只需使用接口的模拟实现。就像一个魅力,尽管我确实想知道我是否在这里过度连接......


答案 1

保护自己免受被嘲笑的课程。创建一个接口,用于执行您真正想要的操作(例如,隐藏根本不涉及外部进程的事实),或者仅用于进程和进程构建器。

您不想测试,ProcessBuilder和Process工作,只是您可以使用它们的输出。当您创建一个接口时,一个普通的实现(可以轻松检查)委托给 ProcessBuilder 和 Process,另一个实现模拟此行为。稍后,您甚至可能有另一个实现,无需启动另一个过程即可完成所需的操作。


答案 2

使用较新版本的JMockit(0.98 +),您应该能够轻松模拟JRE类,如Process和ProcessBuilder。因此,无需创建仅用于测试的接口...

完整示例(使用 JMockit 1.16):

public class MyProcessTest
{
    public static class MyProcess {
        public byte[] run() throws IOException, InterruptedException {
            Process process = new ProcessBuilder("my.exe").start();
            process.waitFor();

            // Simplified example solution:
            InputStream processOutput = process.getInputStream();
            byte[] output = new byte[8192];
            int bytesRead = processOutput.read(output);

            return Arrays.copyOf(output, bytesRead);
        }
   }

    @Test
    public void runProcessReadingItsOutput(@Mocked final ProcessBuilder pb)
        throws Exception
    {
        byte[] expectedOutput = "mocked output".getBytes();
        final InputStream output = new ByteArrayInputStream(expectedOutput);
        new Expectations() {{ pb.start().getInputStream(); result = output; }};

        byte[] processOutput = new MyProcess().run();

        assertArrayEquals(expectedOutput, processOutput);
    }
}

推荐