How is the unit test Java method that uses ProcessBuilder and Process?

I have a Java method that starts a Process with a ProcessBuilder and translates its output into a byte array, and then returns its byte array when the process is complete.

Pseudo Code:

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

What would be the best way to unit test this method? I have not found a way to mock ProcessBuilder (final), even with the incredibly awesome JMockit , it gives me a 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) 

Any thoughts?


The answer . As Olaf recommended, I ended up refactoring these lines with an interface

 Process start(String param) throws IOException; 

Now I am passing an instance of this interface to the class that I wanted to test (in its constructor), usually using the default implementation with the source strings. When I want to test, I just use the mock interface implementation. It works like a charm, although I really wonder if I messed up here ...

+7
java unit-testing mocking jmockit
source share
2 answers

Protect yourself from classes to be mocked. Create an interface in order to do what you really want (for example, hide the fact that external processes are involved at all) or only for Process and ProcessBuilder.

You do not want to verify that ProcessBuilder and Process work only so that you can work with their output. When you create an interface, one trivial implementation (which can be easily verified) delegates ProcessBuilder and Process, another implementation mocks this behavior. Later, you may even have another implementation that does what you need without starting another process.

+10
source share

With newer versions of JMockit (0.98+) you can easily mock JRE classes such as Process and ProcessBuilder. Therefore, there is no need to create interfaces just for testing ...

Full example (using 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); } } 
+2
source share

All Articles