无法在junit中模拟BufferedWriter类

时间:2015-01-13 05:41:39

标签: java unit-testing junit mocking

我在源代码中使用了BufferedWriter对象

BufferedWriter outputToErrorFile = new BufferedWriter(new FileWriter(file));
outputToErrorFile.append("some string");

我试图在我的测试用例中嘲笑它如下:

BufferedWriter mockBufferedWriter = PowerMockito.mock(BufferedWriter.class);
PowerMockito.whenNew(BufferedWriter.class).withAnyArguments().thenReturn(mockBufferedWriter);
PowerMockito.when(mockBufferedWriter.append(Mockito.any(String.class))).thenThrow(new IOException());

但是,BufferedWriter不会被模拟,它总是进入实际实现。是因为它不能模拟BufferedWriter,因为它是一个具体的类?这是否意味着没有一个java.io类可以被模拟?有没有办法嘲笑它,或者有什么我做错了吗?

3 个答案:

答案 0 :(得分:2)

您可以使用JMockit库来模拟Java IO类(包括它们的构造函数,以便将来的实例也被模拟),尽管您可能会遇到诸如NullPointerException构造函数中的Writer()之类的困难(取决于关于如何完成模拟,以及模拟了哪些IO类。

但请注意,Java IO API包含许多交互类和深层继承层次结构。在您的示例中,FileWriter类也可能需要进行模拟,否则将创建实际文件。

此外,在应用程序代码中使用IO类通常只是一个实现细节,可以很容易地更改。例如,您可以从IO流切换到编写器,从常规IO切换到NIO,或使用新的Java 8实用程序。或者使用第三方IO库。

总而言之,尝试模拟IO类只是一个非常糟糕的主意。如果(如另一个答案所示)您将客户端代码更改为{<1}}等等,注入进入SUT,情况会更糟。依赖注入不仅仅是针对这种事情。

相反,在本地文件系统中使用真实的文件,最好是从测试后可以删除的测试目录,和/或仅在阅读时使用固定资源文件。本地文件快速可靠,可带来更多有用的测试。某些开发人员会说,如果测试涉及文件系统,测试不是单元测试,但这只是教条式的建议。

答案 1 :(得分:1)

只需使用普通的mockito。由于BufferedWriter不是最终版,因此无需在此处使用PowerMockito。

使用普通的mockito,你可以写:

final BufferedWriter writer = mock(BufferedWriter.class);
final IOException exception = new IOException();

doThrow(exception).when(writer).append(anyString());

当然,如果您的方法本身已初始化BufferedWriter,并且您没有方法为给定文件返回它,那么您将遇到问题(顺便说一句,您应该使用{ {1}}和Files.newBufferedWriter())。

然而,设计一个真正的解决方案需要您显示正在测试的代码。

答案 2 :(得分:0)

我根本不会嘲笑BufferedWritter。在测试中创建一个由ByteArrayOutputStream支持的编写器,将其传递给您正在测试的类,然后在完成后检查它。这可能需要重构您的代码以创建测试接缝。这是一个可能的实现:

public void writeToWriter(Writer writer) {
  writer.append("some string");
}

然后你的测试看起来像这样:

ByteArrayOutputStream stream = new ByteArrayOutputStream();
OutputStreamWriter writer = new OutputStreamWriter(stream, charset);

objectUnderTest.writeToWriter(writer);

String actualResult = writer.toString(charset);
assertEquals("some string", actualResult);

如果您正在测试的方法需要打开和关闭流,我非常喜欢使用Guava的CharSink类:

public void writeToSink(CharSink sink) {
  try (Writer writer = sink.openBufferedStream()) {
    writer.append("some string");
  }
}