如何在编写 JUnit 时绕过 Runtime.getRuntime()



我有一个类,其中 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();
   }
}

相关内容

  • 没有找到相关文章

最新更新