如何为ProcessBuilder编写包含失败用例的单元测试
跨平台始终返回非零退出码的命令
确实存在适配所有Java测试环境的始终失败命令,因为运行Java单元测试的环境必然预装JRE,可直接调用Java命令执行不存在的主类,固定返回非零退出码,无需区分操作系统:
ProcessBuilder processBuilder = new ProcessBuilder("java", "NonExistentClassNeverExist"); // 合并输出流避免子进程缓冲区满导致阻塞 processBuilder.redirectErrorStream(true); Process process = processBuilder.start(); // 消费流避免进程卡住 try (InputStream is = process.getInputStream()) { is.readAllBytes(); } final int exitCode = process.waitFor(); assert exitCode != 0;
更优的测试方案
直接调用真实命令依赖进程调度,执行速度慢且存在偶发失败风险,更推荐以下测试方案:
- 方案1:封装抽象层解耦(最推荐)
把ProcessBuilder的操作封装为自定义接口,业务代码依赖接口而非直接依赖ProcessBuilder,测试时直接mock接口即可自定义返回结果,无任何环境依赖:
单元测试示例:// 抽象接口 public interface ProcessLauncher { Process start(String... command) throws IOException; } // 生产环境实现 public class DefaultProcessLauncher implements ProcessLauncher { @Override public Process start(String... command) throws IOException { return new ProcessBuilder(command).start(); } }ProcessLauncher mockLauncher = Mockito.mock(ProcessLauncher.class); Process mockProcess = Mockito.mock(Process.class); // 模拟进程返回非零退出码 Mockito.when(mockProcess.waitFor()).thenReturn(1); Mockito.when(mockLauncher.start(Mockito.any())).thenReturn(mockProcess); // 将mockLauncher注入业务类即可执行测试 - 方案2:升级Mockito版本支持final类mock
Mockito 2.x及以上版本已原生支持mock final类,只需在src/test/resources/mockito-extensions目录下新增org.mockito.plugins.MockMaker文件,写入内容mock-maker-inline,即可直接mock ProcessBuilder和Process类,无需修改业务代码。 - 方案3:使用PowerMock扩展能力
若无法升级Mockito 1.x版本,可引入PowerMock依赖,通过@PrepareForTest(ProcessBuilder.class)注解开启final类mock能力,缺点是依赖较重,容易和其他测试框架产生兼容性问题。
内容的提问来源于stack exchange,提问作者Volodya Lombrozo
相关产品推荐
相关产品推荐

