Как выполнить модульное тестирование метода Java, использующего ProcessBuilder и Process?
-
03-07-2019 - |
Вопрос
У меня есть метод 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;
Теперь я передаю экземпляр этого интерфейса в класс, который хочу протестировать (в его конструктор), обычно используя реализацию по умолчанию с исходными строками.Когда я хочу протестировать, я просто использую макетную реализацию интерфейса.Работает как шарм, хотя мне интересно, не слишком ли я здесь взаимодействую...
Решение
Ограждайте себя от занятий, на которых можно смеяться.Создайте интерфейс для выполнения того, что вы действительно хотите (например,скрытие факта участия внешних процессов вообще) или только для 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);
}
}