我有一个类,其中 Runtime.getRuntime() 用于从命令行执行脚本并获取结果以进行进一步处理。
但是当我为这个类编写 JUnit 时,我找不到模拟/避免这个 Runtime.getRuntime().exec() 的方法。
我不能使用 EasyMock 或 PowerMock 或Mockito以外的任何其他模拟 API 。
请给我一个解决这个问题的方法,因为这会影响代码覆盖率。
我有一个类,其中 Runtime.getRuntime() 用于从命令行执行脚本并获取结果以进行进一步处理。
但是当我为这个类编写 JUnit 时,我找不到模拟/避免这个 Runtime.getRuntime().exec() 的方法。
我不能使用 EasyMock 或 PowerMock 或Mockito以外的任何其他模拟 API 。
请给我一个解决这个问题的方法,因为这会影响代码覆盖率。
你必须重构。提取Runtime.getRuntime().exec()
到一个单独的类:
public class Shell {
public Process exec(String command) {
return Runtime.getRuntime().exec(command);
}
}
现在以某种方式向您的测试类调用getRuntime()
显式注入类:Shell
public class Foo {
private final Shell shell;
public Foo(Shell shell) {
this.shell = shell;
}
//...
shell.exec(...)
}
在 JUnit 测试中,只需将模拟Shell
类传递给构造函数即可:
@Mock
private Shell shellMock;
new Foo(shellMock);
旁注:是的,我不是用一种实现来创建Shell
接口。你介意吗?Mockito 不是。奖励:您现在可以验证是否调用了正确的过程:
verify(shellMock).exec("/usr/bin/gimp");
Tomasz Nurkiewicz 提供了一个绝妙的答案。但有一个替代方案。
您可以Runtime.getRuntime()
在您正在测试的类中进入它自己的方法;然后用 Mockito 间谍对其进行测试。在 Mockito 间谍中,您可以使用创建 mock 的版本覆盖此特定方法Runtime
,然后用它做任何您喜欢的事情。
然而,Tomasz 的方法更简洁,我彻底推荐它。我只是想说还有另一种方法可以做到这一点,而无需求助于 Powermock。
我使用了子类化和覆盖(如 Michael C. Feathers 的书“有效使用遗留代码”中所述)
假设我们有以下代码:
public class CannotChange{
public CannotChange(){}
public void TryingToTest() throws IOException {
...
Process proc = Runtime.getRuntime().exec("command");
...
}
}
将运行时代码提取到受保护的方法中:
public void TryingToTest() throws IOException {
...
Process proc = execute("command");
...
}
protected Process execute(string command){
return Runtime.getRuntime().exec(command);
}
然后在测试类中创建一个扩展CannotChange类并覆盖执行的类
public class CannotChangeTest{
private class CannotChangeForTesting extends CannotChange{
public CannotChangeForTesting(){ super(); }
@Override
public Process execute(String command){
return null; //or a mocked object
}
}
@Test
public void TestMethod(){
CannotChange cannotChange = new ForTestCannotChange();
cannotChange.TryingToTest();
}
}