如何对使用 ProcessBuilder 和 Process 的 Java 方法进行单元测试?
我有一个 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)
有什么想法吗?
答案 - 正如 Olaf 所建议的,我最终将这些行重构为一个接口,
Process start(String param) throws IOException;
现在我将该接口的一个实例传递到我想要测试的类中(在其构造函数中),通常使用默认实现原始线路。 当我想测试时,我只需使用接口的模拟实现。 就像一个魅力,虽然我确实想知道我是否在这里过度交互......
I have a Java method which starts up a Process with ProcessBuilder, and pipes its output into a byte array, and then returns its byte array when the process is finished.
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 go about unit testing this method? I haven't found a way to mock ProcessBuilder (it's 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?
Answer - As Olaf recommended, I ended up refactoring those lines to an interface
Process start(String param) throws IOException;
I now pass an instance of this interface into the class I wanted to test (in its constructor), normally using a default implementation with the original lines. When I want to test I simply use a mock implementation of the interface. Works like a charm, though I do wonder if I'm over-interfacing here...
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
保护自己免受嘲笑。 创建一个接口来执行您真正想要的操作(例如隐藏涉及外部进程的事实)或仅用于 Process 和 ProcessBuilder。
您不想测试 ProcessBuilder 和 Process 是否工作,只想测试您可以使用它们的输出。 当您创建接口时,一个简单的实现(可以轻松检查)委托给 ProcessBuilder 和 Process,另一个实现会模拟此行为。 稍后您甚至可能有另一个实现来完成您需要的操作,而无需启动另一个进程。
Shield yourself from the classes to be mocked. Create an interface either for doing what you really want (e.g. hiding the fact that external processes are involved at all) or only for Process and ProcessBuilder.
You don't want to test, that ProcessBuilder and Process work, only that you can work with their output. When you create an interface one trivial implementation (that can be inspected easily) delegates to ProcessBuilder and Process, another implementation mocks this behaviour. Later on you might even have another implementation that does what you need without starting another process.
使用较新版本的 JMockit (0.98+),您应该能够轻松模拟 JRE 类,例如 Process 和 ProcessBuilder。 因此,无需仅为测试而创建接口...
完整示例(使用 JMockit 1.16):
With newer releases of JMockit (0.98+) you should be able to easily mock JRE classes like Process and ProcessBuilder. So, no need to create interfaces just for testing...
Full example (using JMockit 1.16):