1. 概述
Mockito 是一个广泛使用的 Java 应用程序单元测试框架。它提供了各种API来模拟对象的行为。在这个教程中,我们将探讨如何使用 doAnswer()
和 thenReturn()
的桩插件技术,并进行比较。这两种API都可以用于方法的桩插件或模拟,但在某些情况下,我们只能选择其中一个。
2. 依赖项
我们的代码将使用 Mockito 与 JUnit 5 进行示例代码演示,需要在 pom.xml
文件中添加一些依赖:
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.11.0</version>
<scope>test</scope>
</dependency>
您可以在 Maven 中央仓库、JUnit 5 引擎库 和 Mockito 库 找到相关的API库。
3. 使用 thenReturn()
桩插件方法
在 Mockito 中,我们可以使用 thenReturn()
桩插件技术来模拟返回值的方法。 为了演示,我们将使用 thenReturn()
和 doAnswer()
来测试列表的 get()
和 add()
操作:
public class BaeldungList extends AbstractList<String> {
@Override
public String get(final int index) {
return null;
}
@Override
public void add(int index, String element) {
// no-op
}
@Override
public int size() {
return 0;
}
}
在上面的示例代码中,get()
方法返回一个 String
。首先,我们将使用 thenReturn()
桩插件 get()
方法,并通过断言其返回值与桩插件方法相同来验证调用:
@Test
void givenThenReturn_whenGetCalled_thenValue() {
BaeldungList myList = mock(BaeldungList.class);
when(myList.get(anyInt()))
.thenReturn("answer me");
assertEquals("answer me", myList.get(1));
}
3.1. 使用 thenReturn()
返回多个值
此外,thenReturn()
API 允许在连续调用中返回不同的值。我们可以通过链式调用来返回多个值。而且,我们可以在单个方法调用中传递多个值:
@Test
void givenThenReturn_whenGetCalled_thenReturnChaining() {
BaeldungList myList = mock(BaeldungList.class);
when(myList.get(anyInt()))
.thenReturn("answer one")
.thenReturn("answer two");
assertEquals("answer one", myList.get(1));
assertEquals("answer two", myList.get(1));
}
@Test
void givenThenReturn_whenGetCalled_thenMultipleValues() {
BaeldungList myList = mock(BaeldungList.class);
when(myList.get(anyInt()))
.thenReturn("answer one", "answer two");
assertEquals("answer one", myList.get(1));
assertEquals("answer two", myList.get(1));
}
4. 使用 doAnswer()
桩插件 void 方法
add()
方法是一个不返回任何值的 void
方法。由于 thenReturn()
桩插件无法用于 void
方法,我们不能使用它来桩插件 add()
方法。相反,我们将使用 doAnswer()
,因为它允许桩插件 void
方法。 所以,我们将使用 doAnswer()
来桩插件 add()
方法,当调用 add()
方法时,提供的 Answer
将被调用:
@Test
void givenDoAnswer_whenAddCalled_thenAnswered() {
BaeldungList myList = mock(BaeldungList.class);
doAnswer(invocation -> {
Object index = invocation.getArgument(0);
Object element = invocation.getArgument(1);
// verify the invocation is called with the correct index and element
assertEquals(3, index);
assertEquals("answer", element);
// return null as this is a void method
return null;
}).when(myList)
.add(any(Integer.class), any(String.class));
myList.add(3, "answer");
}
在 doAnswer()
中,我们验证 add()
方法的调用,并断言它被调用时的参数符合预期。
4.1. 使用 doAnswer()
桩插件非 void
方法
由于我们可以使用返回值而非 null
的 Answer
来桩插件非 void
方法,因此可以使用 doAnswer()
方法来桩插件此类方法。例如,我们将测试 get()
方法,通过 doAnswer()
桩插件并返回一个返回 String
的 Answer
:
@Test
void givenDoAnswer_whenGetCalled_thenAnswered() {
BaeldungList myList = mock(BaeldungList.class);
doAnswer(invocation -> {
Object index = invocation.getArgument(0);
// verify the invocation is called with the index
assertEquals(1, index);
// return the value we want
return "answer me";
}).when(myList)
.get(any(Integer.class));
assertEquals("answer me", myList.get(1));
}
4.2. 使用 doAnswer()
返回多个值
需要注意的是,我们只能在 doAnswer()
方法中返回一个 Answer
。然而,我们可以在 doAnswer()
方法中放置条件逻辑,根据调用的参数返回不同的值。因此,在下面的示例代码中,我们将根据调用 get()
方法时的索引返回不同的值:
@Test
void givenDoAnswer_whenGetCalled_thenAnsweredConditionally() {
BaeldungList myList = mock(BaeldungList.class);
doAnswer(invocation -> {
Integer index = invocation.getArgument(0);
switch (index) {
case 1:
return "answer one";
case 2:
return "answer two";
default:
return "answer " + index;
}
}).when(myList)
.get(anyInt());
assertEquals("answer one", myList.get(1));
assertEquals("answer two", myList.get(2));
assertEquals("answer 3", myList.get(3));
}
5. 总结
Mockito 框架提供了多种桩插件/模拟技术,如 doAnswer()
、doReturn()
、thenReturn()
、thenAnswer()
等,以方便处理各种类型的 Java 代码及其测试。我们使用了 doAnswer()
和 thenReturn()
来桩插件非 void
方法,并进行了类似的测试。然而,我们只能使用 doAnswer()
来桩插件 void
方法,因为 thenReturn()
方法无法执行此功能。 如往常一样,所有代码示例可在 GitHub 上找到。