Java 6 中的单元测试最终受阻

发布于 2024-12-25 20:01:37 字数 461 浏览 2 评论 0原文

在检查我的代码覆盖率时,我注意到很多单元测试无法检查尝试关闭finally块中打开的InputStreams的finally块。

一个示例摘录是:

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

是否有任何适当的解决方案可以使用 JUnit4 检查 finally 块内的所有内容?

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

While reviewing my code coverage i noticed a lot of Unit tests fail to check finally blocks which try to close open InputStreams in finally blocks.

One Example excerpt is:

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

Is there any appropriate solution to check everything inside the finally block using JUnit4 ?

I know that a code coverage of 100% is not achievable while keeping maximum productivity in mind. However these red lines are sort of an eyecatcher in the report.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

旧城烟雨 2025-01-01 20:01:37

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

  try {
      f = new BufferedInputStream(new FileInputStream(source));
      f.read(buffer);
  } finally {
      IOUtils.closeQuietly(f);
  }

现在变得很困难。 “正确”的方法是将 BufferedInputStream 的创建外部化到另一个类中并注入模拟。通过模拟,您可以验证是否调用了适当的 close() 方法。

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

  try {
      f = fileSystem.open(source);
      f.read(buffer);
  } finally {
      IOUtils.closeQuietly(f);
  }

其中 fileSystem 是 < code>FileSystem 接口,在生产代码或模拟中注入简单的实际实现以进行测试。

interface FileSystem {

    InputStream open(String file);

}

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

有了这个接口,您就可以使用模拟实例化您的测试代码(使用 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();

First of all consider using IOUtils.closeQuietly(), which will reduce your untested code (and probably duplication) into:

  try {
      f = new BufferedInputStream(new FileInputStream(source));
      f.read(buffer);
  } finally {
      IOUtils.closeQuietly(f);
  }

Now it becomes hard. The "right" way would be to externalize the creation of BufferedInputStream into another class and inject mock. Having a mock you can verify whether appropriate close() method was invoked.

@JeffFoster's answer is pretty close to what I mean, however I would recommend composition over inheritance (at the expense of more code):

  try {
      f = fileSystem.open(source);
      f.read(buffer);
  } finally {
      IOUtils.closeQuietly(f);
  }

Where fileSystem is an instance of FileSystem interface with simple real implementation injected in production code or mock for testing.

interface FileSystem {

    InputStream open(String file);

}

Another benefit of externalizing file opening is that if you one decide to remove buffering or add encryption, there is just a single place to modify.

Having that interface you instantiate your test code with mocks (using 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();
梦初启 2025-01-01 20:01:37

您可以将代码重构

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) { }
    }
  }
}

为类似这样的代码

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));
  }
}

,现在您可以编写测试来捕获输入流类并验证其是否已关闭。 (代码很粗糙,但希望您能了解总体思路)。

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());
}

这是否值得是另一个问题!

You could refactor the code a little

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) { }
    }
  }
}

To something like this

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));
  }
}

And now you can write your test to capture the input stream class and verify that is closed. (code is rough, but hopefully you get the general idea).

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());
}

Whether this is worth it or not is a different question!

半枫 2025-01-01 20:01:37

您应该注入一个模拟的 BufferedInputStream - 或使用工厂创建它 - 当调用模拟的 close() 方法时,然后抛出 IOException

另外,我不会在上面的finally 块中添加任何逻辑,除非其中没有任何逻辑。

You should inject a mocked BufferedInputStream - or create it with a factory - and when the mock's close() method is called then throw an IOException.

Additionally, I won't that the finally block above until you don't any logic in there.

小梨窩很甜 2025-01-01 20:01:37

我认为您需要问自己这是否真的值得进行测试。一些测试迷往往会错过尝试达到 100% 测试覆盖率所带来的收益递减。在这种情况下,看起来一些建议的解决方案为实际代码添加了更多复杂性,以使其“可测试”。我对复杂的测试代码很满意,但是仅仅为了使其“可测试”而向实际代码添加复杂性对我来说是一个糟糕的主意。

I think you need to ask yourself whether that is truly worth the testing effort. Some testing junkies tend to miss the diminishing returns of trying to achieve ~100% test coverage. In this case it looks like some of the proposed solutions add more complexity to the actual code in order to make it "testable". I'm fine with complex test code, but adding complexity to the actual code just to make it "testable" strikes me as a terrible idea.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文