Mockito @Mock, @Spy, @Captor 简介
在 Java 的生态里,Mockito 可以说是单元测试比不可少的工具。在测试中,我们需要频繁地构造外部依赖类。通过控制外部依赖类的行为,来在单元测试中检查代码的不同运行路径。借助 Mockito,我们可以轻易地构造一个虚假的类实例,控制类方法的返回值、是否抛出异常等行为,又或者检查方法被调用了多少次,被调用时的输入参数的行为。
下面,让我来简单地介绍 Mockito 的通常用法。
构造返回值
@Test
void mockitoTest() {
Document document = mock(Document.class);
when(document.getName()).thenReturn("Mockito");
assertThat(document.getName()).isEqualTo("Mockito");
}
根据不同入参返回不同结果
@Test
void mockitoTest() {
Document document = mock(Document.class);
when(document.getChapterName(0)).thenReturn("Overview");
when(document.getChapterName(1)).thenReturn("Why and How");
assertThat(document.getChapterName(0)).isEqualTo("Overview");
assertThat(document.getChapterName(1)).isEqualTo("Why and How");
}
抛出异常
@Test
void mockitoTest() {
Document document = mock(Document.class);
when(document.getName()).thenThrow(new IllegalArgumentException("Oops!"));
assertThatThrownBy(() -> {
document.getName();
})
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Oops!");
}
测试方法是否被调用
@Test
void mockitoTest() {
Document document = mock(Document.class);
document.getName();
verify(document).getName();
}
检查方法是否被调用指定次数
@Test
void mockitoTest() {
Document document = mock(Document.class);
document.getName();
document.getName();
document.getName();
verify(document, times(3)).getName(); // 测试 getName() 是否被调用3次
}
当你调用 verify() 方法而不带 times() 时,默认为 times(1)。
检查方法被调用时的入参
@Test
void mockitoTest() {
Document document = mock(Document.class);
document.getChapterName(9);
verify(document).getChapterName(9);
}
检查方法的调用顺序
@Test
void mockitoTest() {
Document document = mock(Document.class);
document.getName();
document.getChapterName(0);
document.getChapterName(1);
// 检查程序是否按 getName(), getChapterName(0), getChapterName(1)
// 的顺序调用对应方法
InOrder inOrder = inOrder(document);
inOrder.verify(document).getName();
inOrder.verify(document).getChapterName(0);
inOrder.verify(document).getChapterName(1);
}
Spy
spy() 方法用于监控一个已有的实例。你不能像控制通过mock() 构造出来的实例一样控制其行为,但是你依旧可以 verify() 被监控实例的各种行为
@Test
void mockitoTest() {
Document document = new Document();
Document spyDocument = spy(document);
spyDocument.getChapterName(0);
verify(spyDocument).getChapterName(0);
}
Captor
Captor 用于捕获实例的传入参数。虽然 verify() 提供了检查方法入参的途径,但是有时候你会希望你能保存传入参数,方便后续更多的操作。例如,你要检查的传入参数是一个复杂的大对象。简单的一行 verify() 不能满足你的要求。又或者传入参数包含了一部分动态数据(例如当前时间的时间戳),你需要更复杂的测试逻辑而 verify() 不能满足要求。
不过下面的例子,为了方便,只以 String 为例。
@Test
void mockitoTest() {
Document document = new Document();
Document spyDocument = spy(document);
spyDocument.setName("Cats and Dogs");
ArgumentCaptor<String> arg = ArgumentCaptor.forClass(String.class);
verify(spyDocument).setName(arg.capture());
assertThat(arg.getValue()).isEqualTo("Cats and Dogs");
}