8

我有一个类,其中 Runtime.getRuntime() 用于从命令行执行脚本并获取结果以进行进一步处理。

但是当我为这个类编写 JUnit 时,我找不到模拟/避免这个 Runtime.getRuntime().exec() 的方法。

我不能使用 EasyMock 或 PowerMock 或Mockito以外的任何其他模拟 API 。

请给我一个解决这个问题的方法,因为这会影响代码覆盖率。

4

3 回答 3

18

你必须重构。提取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");
于 2012-06-01T08:08:11.173 回答
3

Tomasz Nurkiewicz 提供了一个绝妙的答案。但有一个替代方案。

您可以Runtime.getRuntime()在您正在测试的类中进入它自己的方法;然后用 Mockito 间谍对其进行测试。在 Mockito 间谍中,您可以使用创建 mock 的版本覆盖此特定方法Runtime,然后用它做任何您喜欢的事情。

然而,Tomasz 的方法更简洁,我彻底推荐它。我只是想说还有另一种方法可以做到这一点,而无需求助于 Powermock。

于 2012-06-03T09:10:51.133 回答
0

我使用了子类化和覆盖(如 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();
   }
}
于 2013-10-23T12:44:28.877 回答