基础知识
常用注解含义
- @Test:标记一个方法为测试方法
- @BeforeEach:标记的方法会在每个测试方法执行前执行
- @AfterEach:标记的方法会在每个测试方法执行后执行
- @BeforeAll:标记的方法会在所有测试方法执行前执行一次
- @AfterAll:标记的方法会在所有测试方法执行后执行一次
- @Disabled:用于禁用一个测试方法或测试类
- Assertions:JUnit Jupiter 提供了一系列的断言方法,用于测试你的代码的行为。例如:assertEquals(expected, actual),assertTrue(condition)等
- @ParameterizedTest:用于定义参数化测试,可以用不同的参数多次运行同一个测试方法
- @ExtendWith:用于定义一个扩展类,扩展类可以改变测试的行为,例如提供依赖注入、处理异常等
初始化Mock对象的三种方式
-
@ExtendWith(MockitoExtension.class) + @Mock等注解
-
Mockito.mock(X.class)
-
MockitoAnnotations.openMocks(this) + @Mock等注解
常见的Mock方法的方式
a. thenReturn()/doReturn():当需要根据特定的调用来返回一个固定的值
when(mockObject.method()).thenReturn(value);
或 doReturn(value).when(mockObject).method();
b. thenThrow()/doThrow():当想在方法被调用时抛出一个异常
when(mockObject.method()).thenThrow(new RuntimeException());
或 doThrow(new RuntimeException()).when(mockObject).method();
c. thenAnswer()/doAnswer():当需要基于输入的参数来返回值,或者需要多次调用返回不同的值
when(mockObject.method(anyString())).thenAnswer(invocation -> {
Object arg0 = invocation.getArgument(0);// do something with arg0return null; });
或
doAnswer(invocation -> {
Object arg0 = invocation.getArgument(0);// do something with arg0return null;
}).when(mockObject).method(anyString());
d. thenCallRealMethod()/doCallRealMethod():当想在调用一个mock对象的方法时,真的调用实现该方法的代码
when(mockObject.method()).thenCallRealMethod();
或:doCallRealMethod().when(mockObject).method();
e. doNothing():这个用于定义一个方法在被调用时什么都不做,通常用于 mock 一些返回 void 的方法
doNothing().when(mockedList).clear();
注意:doXxx().when(obj).someMethod(); 与 when(obj.someMethod()).thenXxx() 这两种结构在大部分情况下可以互换使用,但在某些特定情况下,它们之间存在一些差异。以下是一些关键的区别:
-
对于非 void 方法,doReturn().when() 结构可以用在 mock 的方法在调用时抛出异常的情况下,而 when().thenReturn() 结构不能。这是因为 when().thenReturn() 结构会首先调用实际的方法,如果该方法抛出异常,那么程序就不能正常工作。而 doReturn().when() 结构不会调用实际的方法,所以即使方法抛出异常程序也能正常工作
<font style="background-color:#D8DAD9;">doReturn("mocked value").when(mock).method(); // 这里不会调用实际的方法</font>
-
对于 void 方法,doReturn().when() 结构不能用,需要使用 doNothing().when(mock).method(),doThrow().when(mock).method() 或 doAnswer().when(mock).method() 等结构
doNothing().when(mock).method(); // 对于 void 方法,需要使用 doNothing()
因此,根据需求和方法的特性(是否为 void,是否在调用时抛出异常),可以选择适当的结构,在大部分情况下,when().thenReturn() 结构更简洁,更易读,推荐使用。但如果需要处理特殊情况,如上述的情况,就需要使用 doReturn().when() 结构
常见的断言方式
在JUnit 5中,我们通常使用org.junit.jupiter.api.Assertions类中的静态方法来进行断言。以下是几种常见的断言方式:
- assertTrue(boolean condition)/assertFalse(boolean condition):断言条件是否为真
例如:assertFalse(5 < 4);
- assertEquals(Object expected, Object actual)/assertNotEquals(Object unexpected, Object actual):断言两个对象是否相等
例如:assertEquals(5, 5);
- assertNull(Object actual)/assertNotNull(Object actual):断言对象s是否为空
assertNotNull(new Object());
- assertSame(Object expected, Object actual)/assertNotSame(Object unexpected, Object actual):断言两个对象是否引用同一个对象
String str1 = “test”;
String str2 = str1;
assertSame(str1, str2);
- assertArrayEquals(Object[] expected, Object[] actual):断言两个数组是否相等
assertArrayEquals(new int[] {1, 2, 3}, new int[] {1, 2, 3});
- assertThrows(Class<? extends Throwable> expectedType, Executable executable):断言执行某段代码是否会抛出特定类型的异常。
assertThrows(NullPointerException.class, () -> {
throw new NullPointerException();
});
此外,Mockito 的 verify() 方法被用来确认某个特定的方法是否被调用了指定的次数
例如,如果想确认某个mock对象的某个方法是否被调用过:
Mockito.verify(mockObject).method();
如果想确认某个方法被调用了特定的次数:
Mockito.verify(mockObject, times(5)).method();
如果想确认某个方法从未被调用过:
Mockito.verify(mockObject, never()).method();
覆盖度查看
源代码中绿色区域为已被测试覆盖,红色区域为未被测试覆盖
注意:IDEA和JaCoCo计算代码覆盖率的方式略有不同,例如:JaCoCo提供了分支覆盖,而IDEA可能只提供了线性代码覆盖,假如代码中包含了复杂的条件逻辑,那么这可能会导致覆盖率的差异。在IDEA中看到的覆盖度会比在JaCoCo中的高