Как тестировать модульный метод 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);
}
}