我有一个类,其中 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
接口。你介意吗?莫基托不是。奖励:您现在可以验证是否调用了正确的进程:
verify(shellMock).exec("/usr/bin/gimp");
Tomasz Nurkiewicz提供了一个精彩的答案。 但还有另一种选择。
您可以在要测试的类中将Runtime.getRuntime()
移动到它自己的方法中;然后使用 Mockito 间谍对其进行测试。 在 Mockito 间谍中,您可以使用创建模拟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();
}
}