Как тестировать модульный метод Java, который использует ProcessBuilder и Process?

у меня есть метод Java, который запускает процесс с ProcessBuilder и передает его вывод в массив байтов, а затем возвращает его массив байтов, когда процесс завершен.

псевдо-код:

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 ответов


защитите себя от классов, которые будут высмеиваться. Создайте интерфейс либо для того, что вы действительно хотите (например, скрывая тот факт, что внешние процессы участвуют вообще), либо только для Process и ProcessBuilder.

вы не хотите тестировать, что ProcessBuilder и Process работают, только то, что вы можете работать с их выходом. При создании интерфейса одна тривиальная реализация (которую можно легко проверить) делегирует ProcessBuilder и Process, другая реализация высмеивает такое поведение. Позже у вас может даже быть другая реализация, которая делает то, что вам нужно, без запуска другого процесса.


С более новыми выпусками 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);
    }
}