如果您可以将 Mockito 与 JUnit 结合使用,您的测试可能如下所示:
public void onMessage_Success() throws Excepton {
// Arrange
Message message = aMessage().withContent("...").create();
File mockLogFile = mock(File.class);
MessageHandler mockMessageHandler = mock(MessageHandler.class);
when(mockMessageHandler).handleMessage(any(MessageType.class)
.thenReturn("somePredefinedTestOutput");
when(mockMessageHandler).getLogFile().thenReturn(mockLogFile);
MessageListener sut = spy(new MessageListener());
Whitebox.setInternalState(sut, "messageHanlder", mockMessageHandler);
// or simply sut.setMessageHandler(mockMessageHandler); if a setter exists
// Act
sut.onMessage(message);
// Assert
assertThat(mockLogFile, contains("your desired content"));
verify(sut, times(1)).handleMessage(any(Message.class));
}
请注意,这只是一个如何测试它的简单示例。可能还有很多其他方法可以测试功能。上面的示例展示了用于生成默认消息的典型构建器模式,默认消息接受某些值进行测试。此外,我还没有真正澄清 mockLogFile 上 contains 方法的 Hamcrest 匹配器。
正如@Keppil 在他的评论中也提到的那样,创建多个测试用例是有意义的,这些测试用例在安排和断言测试坏用例的部分略有不同
我可能解释得不够充分的是 MessageHandler 的 getLogFile() 方法(在您的应用程序中肯定有另一个名称)应该返回对您的 MessageHandler 实例使用的文件的引用来存储实际日志消息。因此,最好将此 mockMessageHandler 定义为 spy(new MessageHandler()) 而不是 mock(MessageHandler.class),尽管这意味着单元测试实际上是一个集成测试,因为同时测试了两个类的交互。
但总的来说,我希望你明白 - 使用 mock(Class) 为你的被测系统 (SUT) 所需的依赖项生成默认实现,如果你想包含真实世界的对象,则使用 spy(Instance)一种只有空值作为返回类型的。您可以使用 when(...).thenReturn(...)/.thenThrow(...) 或 doReturn(...).when(...) 来影响模拟对象的返回值,以防万一无效操作 f.e.
如果你有依赖注入到私有字段,你应该使用Whitebox.setInternalState(...) 将值注入到 sut 或模拟类中,如果没有公共或包私有(如果你获得重用包结构的测试模型测试类中的系统被测类)设置方法可用。
此外,verify(...) 允许您验证在执行 SUT 时是否调用了某个方法。在实际断言不是那么简单的情况下,这在这种情况下非常方便。