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

时间:2008-10-16 07:01:31

标签: java unit-testing mocking jmockit

我有一个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)

有什么想法吗?


回答 - 正如奥拉夫推荐的那样,我最终将这些行重构为接口

Process start(String param) throws IOException;

我现在将此接口的一个实例传递给我想要测试的类(在其构造函数中),通常使用带有原始行的默认实现。当我想测试时,我只需使用接口的模拟实现。虽然我想知道我是否在这里过度接触......但作为魅力的工作方式......

2 个答案:

答案 0 :(得分:10)

保护自己不被嘲笑的类。创建一个接口,用于执行您真正想要的操作(例如,隐藏外部过程完全涉及的事实)或仅用于Process和ProcessBuilder。

您不希望测试,ProcessBuilder和Process工作,只是您可以使用他们的输出。当您创建一个接口时,一个简单的实现(可以很容易地检查)委托给ProcessBuilder和Process,另一个实现会模仿这种行为。稍后您甚至可以使用另一个实现来执行您需要的操作而无需启动另一个进程。

答案 1 :(得分:2)

使用较新版本的JMockit(0.98+),您应该可以轻松地模拟像Process和ProcessBuilder这样的JRE类。因此,无需仅为测试创建接口......

完整示例(使用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);
    }
}