一、Mock
1.1 什么是 Mock
mock 是在测试过程中,对于一些不容易构造/获取的对象,创建一个 mock 对象来模拟对象的行为。
1.2 什么时候使用
* 单元测试时,使用外部资源或第三方库代码
* 并行开发时,另一方还没有开发完毕
1.3 Mock 分类
- Mock 对象
主要适用于单元测试,写入一些预期的值,通过它进行自己想要的测试。
- Mock Server
主要适用于接口和性能测试,构造一个假的服务返回预期的结果,进行测试。
1.4 技术选型
在 Java 阵营 中主要的 Mock 测试工具有 Mockito ,JMock,MockCreator,Mockrunner,EasyMock,MockMaker 等
这里我们采用 Mockito。
二、Mockito实践
2.1 引入依赖
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
2.2 验证行为
@Test
public void testList(){
// 创建模拟对象
List<String> mockList = mock(List.class);
// 模拟对象执行操作
mockList.add("one");
mockList.clear();
// 验证
verify(mockList).add("one");
verify(mockList).clear();
}
执行程序,发现测试通过,证明 Mockito 可以验证行为。
2.3 验证行为调用次数
@Test
public void testListBehaviorExecutionFrequency() {
// 创建模拟对象
List<String> mockList = mock(List.class);
// 模拟对象执行操作
mockList.add("1");
mockList.add("2");
mockList.add("2");
mockList.add("3");
mockList.add("3");
mockList.add("3");
mockList.add("4");
mockList.add("4");
mockList.add("4");
mockList.add("4");
mockList.clear();
// 验证
verify(mockList).add("1");
// 验证执行次数
verify(mockList, times(1)).add("1");
verify(mockList, times(2)).add("2");
verify(mockList, times(3)).add("3");
verify(mockList, times(4)).add("4");
verify(mockList, times(1)).clear();
// 验证从来没有执行的方法
verify(mockList, never()).add("0");
// 至少执行过一次
verify(mockList, atLeastOnce()).add("4");
// 最小执行过2次
verify(mockList, atLeast(2)).add("4");
// 最多执行过5次
verify(mockList, atMost(5)).add("4");
}
2.4 验证行为执行的顺序
@Test
public void testListBehaviorExecutionOrderBySingle(){
// 创建模拟对象
List<String> mockList = mock(List.class);
// 模拟对象执行操作
mockList.add("1");
mockList.add("2");
mockList.add("3");
InOrder inOrder = inOrder(mockList);
// 验证先执行add 1 ,再执行添加 3,可以发现跳过了添加 2 的行为,说明可以只关注感兴趣的交互即可
inOrder.verify(mockList).add("1");
inOrder.verify(mockList).add("3");
}
@Test
public void testListBehaviorExecutionOrderByMultiple() {
// 创建模拟对象
List<String> firsMockList = mock(List.class);
List<String> secondMockList = mock(List.class);
// 模拟对象执行操作
firsMockList.add("firsMockList add ");
secondMockList.add("secondMockList add ");
InOrder inOrder = inOrder(firsMockList,secondMockList);
// 验证执行顺序
inOrder.verify(firsMockList).add("firsMockList add ");
inOrder.verify(secondMockList).add("secondMockList add ");
}
2.5 验证从未调用过的对象
@Test
public void testNoOperating() {
List<String> notOperatingList = mock(List.class);
verifyNoInteractions(notOperatingList);
}
2.6 查找冗余调用
@Test
public void testNoMoreInteractions(){
List<String> mockList = mock(List.class);
mockList.add("one");
mockList.add("two");
verify(mockList).add("one");
//将抛出异常,因为 `mockList.add("two");` 是额外的调用
verifyNoMoreInteractions(mockList);
}
2.7 模拟存根
@Test
public void testStubbing(){
// 创建模拟对象
List mockList = mock(List.class);
// 启用存根方法,当什么时候执行什么操作
when(mockList.get(0)).thenReturn("1");
when(mockList.get(3)).thenReturn("3");
Assert.assertEquals("1",mockList.get(0));
Assert.assertEquals("3",mockList.get(3));
Assert.assertEquals(null,mockList.get(99));
}
说明:
* 默认情况下,对于所有返回值的方法,模拟将酌情返回null,原始/原始包装器值或空集合。例如,对于int / Integer为0,对于boolean / Boolean为false。
* 存根可以被覆盖:例如,通用存根可以进入夹具设置,但是测试方法可以覆盖它。请注意,过多的存根是潜在的代码异味,表明存在过多的存根
* 一旦存根,该方法将始终返回存根值,而不管其被调用了多少次。
* 最后一次存根更为重要-当您多次对具有相同参数的相同方法进行存根时。换句话说:存根的顺序很重要,但很少有意义,例如,存根完全相同的方法调用时或有时使用参数匹配器时,等等。
2.8 模拟异常抛出
@Test
public void testThrowException(){
// 创建模拟对象
List mockList = mock(List.class);
// 指定什么操作将抛出异常
doThrow(new RuntimeException()).when(mockList).clear();
// 该方法将抛出异常
mockList.clear();
}
2.9 模拟创建的简写- @Mock
注释
@RunWith(MockitoJUnitRunner.class)
public class MockTest{
@Mock
private List list;
@Test
public void testMockAnnotation(){
list.add("one");
verify(list).add("one");
}
}
需要配合 @RunWith(MockitoJUnitRunner.class)
注解使用。
更多使用方法请查看 文档