单元测试最终在Java 6中阻塞

fyr*_*fyr 7 java exception-handling junit4 try-catch-finally java-6

在查看我的代码覆盖率时,我注意到很多单元测试无法检查最终块,这些块试图关闭finally块中的打开InputStreams.

一个示例摘录是:

  try {
      f = new BufferedInputStream(new FileInputStream(source));
      f.read(buffer);
  } finally {
      if (f != null)
          try {
              f.close();
          } catch (IOException ignored) {
          }
      }
  }
Run Code Online (Sandbox Code Playgroud)

有没有适当的解决方案来使用JUnit4检查finally块内的所有内容?

我知道在保持最高生产力的同时,无法实现100%的代码覆盖率.然而,这些红线在报告中引人注目.

Tom*_*icz 6

首先考虑使用IOUtils.closeQuietly(),这会将未经测试的代码(可能是重复代码)减少为:

  try {
      f = new BufferedInputStream(new FileInputStream(source));
      f.read(buffer);
  } finally {
      IOUtils.closeQuietly(f);
  }
Run Code Online (Sandbox Code Playgroud)

现在变得艰难." 正确 "的方式是将创建外部BufferedInputStream化为另一个类并注入模拟.有了一个模拟,你可以验证是否close()调用了适当的方法.

@JeffFoster的答案非常接近我的意思,但是我会推荐组合而不是继承(以更多的代码为代价):

  try {
      f = fileSystem.open(source);
      f.read(buffer);
  } finally {
      IOUtils.closeQuietly(f);
  }
Run Code Online (Sandbox Code Playgroud)

哪里fileSystem是一个实例FileSystem,在生产代码或模拟注射测试简单的真正的实现接口.

interface FileSystem {

    InputStream open(String file);

}
Run Code Online (Sandbox Code Playgroud)

外部化文件打开的另一个好处是,如果您决定删除缓冲或添加加密,则只需要修改一个位置.

使用该接口,您可以使用模拟实例化测试代码(使用Mockito):

//given
FileSystem fileSystemMock = mock(FileSystem.class);
InputStream streamMock = mock(InputStream.class);

given(fileSystemMock.open("file.txt")).willReturn(streamMock);

//when
//your code

//then
verify(streamMock).close();
Run Code Online (Sandbox Code Playgroud)


Jef*_*ter 5

你可以稍微重构一下代码

public class TestMe {
  public void doSomething() {
    try {
      f = new BufferedInputStream(new FileInputStream(source));
      f.read(buffer);
    } finally {
      if (f != null)
      try {
          f.close();
      } catch (IOException ignored) { }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

对于这样的事情

public class TestMe {
  public void doSomething() {
    try {
      f = createStream()
      f.read(buffer);
    } finally {
      if (f != null)
      try {
          f.close();
      } catch (IOException ignored) { }
    }
  }

  public InputStream createStream() {
      return new BufferedInputStream(new FileInputStream(source));
  }
}
Run Code Online (Sandbox Code Playgroud)

现在,您可以编写测试来捕获输入流类并验证是否已关闭.(代码粗糙,但希望你得到一般的想法).

public void TestSomething () {
   InputStream foo = mock(InputStream.class); // mock object
   TestMe testMe = new TestMe() {
     @Override
     public InputStream createStream() {
          return foo;
     } 
   }

   testMe.something();

   verify(foo.close());
}
Run Code Online (Sandbox Code Playgroud)

这是否值得,这是一个不同的问题!