目录
添加依赖
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
1. 初始化
1)注解
@RunWith(MockitoJUnitRunner.class)
@Mock相当于:NeedMockClass mockInstatnce = Mockito.mock(NeedMockClass.class);
@Spy相当于:NeedMockClass spyInstatnce = Mockito.spy(new NeedMockClass());
@InjectMocks相当于:被测试类上标记@InjectMocks,Mockito就会实例化该类,并将标记@Mock、@Spy注解的属性值注入到被测试类中。
@InjectMocks的注入顺序:
1、 如果这里的TargetClass中没有显示定义构造方法,Mockito会调用默认构造函数实例化对象,然后依次寻找setter 方法 或 属性(按Mock对象的类型或名称匹配)注入@Mock对象;
2、如果TargetClass中显式定义了有参数的构造函数,那么 就不再寻找setter 方法和 属性注入, Mockito会选择参数个数最多的构造函数实例化并注入@Mock对象(这样可以尽可能注入多的属性);
3、有多个最大构造函数时,Mockito 究竟选择哪一个就混乱了,测试时应该避免这种情况的发生,很容易发生空指针。
2)类反射
2. @InjectMocks 案例
@Service
public class MsgLogService implements IMsgLogService {
@Resource
private MsgLogMapper msgLogMapper;
@Resource
private IApiService apiService;
@Override
public void handle() {
List<MsgLog> list = msgLogMapper.selectUnHandledMsg();
if (CollectionUtils.isEmpty(list)) {
return;
}
for (MsgLog log : list) {
if (StringUtils.isEmpty(log.getMsg())) {
continue;
}
boolean res = apiService.putProfileToHBase(log.getMsg());
if (res) {
msgLogMapper.updateHandleStatus(log);
}
}
}
}
/**
MsgLogService 用@InjectMocks标识,且MsgLogMapper、IApiService用@Mock标识
则MsgLogService内的 MsgLogMapper、apiService 就自动注入
*/
@RunWith(MockitoJUnitRunner.class)
public class MsgLogServiceTest {
@InjectMocks
private MsgLogService msgLogService;
@Mock
private MsgLogMapper msgLogMapper;
@Mock
private IApiService apiService;
public MsgLogServiceTest() {}
@Test
public void handle_request_times_0_01() {
when(msgLogMapper.selectUnHandledMsg()).thenReturn(null);
msgLogService.handle();
verify(apiService, times(0)).putProfileToHBase(anyString());
verify(msgLogMapper, times(0)).updateHandleStatus(any());
}
}
3. 设置测试桩(Stubbing)
when(mockMapper.insert(any())).thenReturn(888);
when(mockMapper.insert(any())).thenThrow(new RuntimeException("db操作异常"));
when(mockService.methodReturnId(any(OrderInfo.class))).thenAnswer(demoAnswer);
doReturn(888).when(mockMapper).insert(any());
上面mock的方法都是有返回值的,为void函数设置桩用以下语法,因为编译器不喜欢void函数在括号内
doNothing().when(mockService).voidMethod(any(String.class), any(Integer.class));
doThrow(new RuntimeException("")).when(mockService).voidMethod(eq("ex"), eq(10001));
doAnswer(demoAnswer).when(mockService).methodVoid(any(OrderInfo.class));
4. Stubbing连缀调用
第一次调用返回1,第二次调用返回2,以下三种写法等价的:
when(mockService.addStr(anyString())).thenReturn("1").thenReturn("2");
when(mockService.addStr(anyString())).thenReturn("1", "2");
doReturn("1").doReturn("2").when(mockService).addStr(anyString());
String relt1 = mockService.addStr("x");
String relt2 = mockService.addStr("x");
String relt3 = mockService.addStr("x");
Assert.assertEquals("1", relt1);
Assert.assertEquals("2", relt2);
Assert.assertEquals("2", relt3); //后续调用一直返回2
第一次调用什么也不做,第二次调用抛出异常:
doNothing().doThrow(new RuntimeException("调用两次了")).when(mockService).methodVoid(any());
mockService.methodVoid(any());
try {
mockService.methodVoid(any());
} catch (Exception e) {
Assert.assertEquals("调用两次了", e.getMessage());
}
下面写法结果就变了,第二次stubbing覆盖第一次的:
when(mockService.addStr(anyString())).thenReturn("1");
when(mockService.addStr(anyString())).thenReturn("2");
String relt1 = mockService.addStr("x");
String relt2 = mockService.addStr("x");
Assert.assertEquals("2", relt1);
Assert.assertEquals("2", relt2);
5. Mock方法的默认值
Mock对象的方法未设置测试桩时,Mockito会返回方法返回类型的默认值,不会报错。mock 实例默认的会给所有的方法添加基本实现:返回 null 或空集合,或者 0 等基本类型的值。这取决于方法返回类型
List mockList = mock(List.class);
when(mockList.get(5)).thenReturn("hello");//打桩
Assert.assertEquals("hello", mockList.get(5));//打桩的情景返回设定值
Assert.assertEquals(null, mockList.get(10));//未打桩的情景不会报错,返回默认值
6. 参数匹配器(matchers)
any(): 任何参数
any(OrderInfo.class): 任何OrderInfo(开发中自定义的类)
anyString(): 任何字符串,等同于any(String.class)
eq(1): 具体值1
InvokeService类中,要被mock的方法
public Integer targetMethod01(String param01, Integer param02) {
return 1;
}
测试类TargetClassTest中:
@Mock
private InvokeService mockService;
@Test
public void dmeoTest() {
when(mockService.targetMethod01(any(), any())).thenReturn(666);
when(mockService.targetMethod01(any(String.class), anyInt())).thenReturn(666);
when(mockService.targetMethod01("demo", 1)).thenReturn(666);
when(mockService.targetMethod01(eq("demo"), eq(1))).thenReturn(666);
// When using matchers, all arguments have to be provided by matchers.
//上面都是正确的,下面的写法,单测执行时会报错
when(mockService.targetMethod01(eq("demo"), 1)).thenReturn(666);
}
7. 行为测试
一旦使用 mock() 或@Mock生成模拟对象,意味着 Mockito 会记录着这个模拟对象调用了什么方法,还有调用了多少次、调用的顺序等。最后由用户决定是否需要进行验证。
- times(x):mock方法被调用x次
- never():从未被调用过,等价于times(0)
- atLeast(x):至少调用过x次
- atLeastOnce():至少调用过1次,等价于atLeast(1)
- atMost(x):最多调用过x次
-
verify 内部跟踪了所有的方法调用和参数的调用情况,然后会返回一个结果,说明是否通过。verify 也可以像 when 那样使用参数匹配器。
目标类TargetClass中: @Autowired private InvokeService invokeService; public void invokeTimes() { invokeService.addStr("1"); invokeService.addStr("2"); invokeService.addStr("2"); } 测试类TargetClassTest中: @InjectMocks TargetClass targetClass; @Mock private InvokeService mockService; @Test public void testTimes() { targetClass.invokeTimes(); verify(mockService).addStr(eq("1"));//times()省略的话,默认验证调用一次 verify(mockService, times(1)).addStr(eq("1")); verify(mockService, times(1)).addStr("1"); verify(mockService, times(2)).addStr("2"); verify(mockService, atLeastOnce()).addStr("1"); verify(mockService, atMost(2)).addStr("2"); verify(mockService, never()).addStr("0"); }
验证Mock方法的调用顺序
目标类TargetClass中:
@Autowired
private InvokeService invokeService;
public void addStr() {
invokeService.add("str01");
invokeService.add("str02");
}
测试类TargetClassTest中:
@InjectMocks
TargetClass targetClass;
@Mock
private InvokeService mockService;
@Test
public void testInOrder_verify() {
InOrder inOrder = inOrder(mockService);
targetClass.addStr();
//验证调用顺序,若是调换两句,将会出错
inOrder.verify(mockService).add("str01");
inOrder.verify(mockService).add("str02");
}
8. Answer
主要用来截获传递给mock方法的参数
目标类TargetClass中:
@Autowired
private InvokeService invokeService;
@Autowired
private InvokeMapper invokeMapper;
public int saveOrder() {
OrderInfo orderInfo = new OrderInfo();
orderInfo.setUserName("sxd");
orderInfo.setDealerId(62669);
orderInfo.setOrderType(3);
int result = invokeMapper.insert(orderInfo);
return result;
}
InvokeMapper中:
int insert(OrderInfo orderInfo);
测试类TargetClassTest中:
@InjectMocks
TargetClass targetClass;
@Mock
private InvokeService mockService;
@Mock
private InvokeMapper mockMapper;
//定义一个Answer类,用于截获mock方法入参
class DemoAnswer implements Answer<Object> {
Object[] params;
@Override
public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
params = invocationOnMock.getArguments(); //方法入参
return 666; //方法返回值
}
public Object[] getParams() {
return params;
}
}
@Test
public void test_insertOrder_param_by_Answer() throws Exception {
DemoAnswer demoAnswer = new DemoAnswer();
doAnswer(demoAnswer).when(mockMapper).insert(any(OrderInfo.class));
int result = targetClass.saveOrder();
Assert.assertEquals(666, result);
OrderInfo saveOrder = (OrderInfo) demoAnswer.getParams()[0];
Assert.assertEquals(3, saveOrder.getOrderType().intValue());
}
9. 有返回值的方法demo
InvokeService类中,需要mock的方法:
public Integer methodReturnId(OrderInfo orderInfo) throws Exception {
return orderInfo.getId();
}
测试类TargetClassTest中:
@Mock
private InvokeService mockService;
@Test
public void testAnswer_when_method_unvoid() throws Exception {
Answer<Integer> demoAnswer = new Answer<Integer>() {
@Override
public Integer answer(InvocationOnMock invocationOnMock) throws Throwable {
OrderInfo orderInfo = (OrderInfo) invocationOnMock.getArguments()[0]; //methodReturnId方法的入参
//orderInfo.setId(2);//截获并自定义入参
if (orderInfo.getId() > 3) {
throw new RuntimeException("大于3了");
}
return 666;//自定义methodReturnId方法的返回值
}
};
//mock的方法是非void时,以下三种写法都可以
//doAnswer(answer).when(mockService).methodReturnId(any(OrderInfo.class)); //doAnswer:执行demoAnswer的answer方法
//when(mockService.methodReturnId(any(OrderInfo.class))).then(demoAnswer); //then(answer): 执行demoAnswer的answer方法
when(mockService.methodReturnId(any(OrderInfo.class))).thenAnswer(demoAnswer); //thenAnswer(answer): 执行demoAnswer的answer方法
OrderInfo paramOrder = new OrderInfo() {{
setId(3);
}};
Integer result = mockService.methodReturnId(paramOrder);
Assert.assertEquals(666, result.intValue());
OrderInfo paramOrder02 = new OrderInfo() {{
setId(4);
}};
try {
mockService.methodReturnId(paramOrder02);
} catch (Exception e) {
Assert.assertEquals("大于3了", e.getMessage());
}
}
10. void方法demo
InvokeService中,需要mock的方法:
public void methodVoid(OrderInfo orderInfo) {
}
测试类中:
@Mock
private InvokeService mockService;
@Test
public void testAnswer_when_method_void() throws Exception {
Answer<Integer> demoAnswer = new Answer<Integer>() {
@Override
public Integer answer(InvocationOnMock invocationOnMock) throws Throwable {
OrderInfo orderInfo = (OrderInfo) invocationOnMock.getArguments()[0];//methodReturnId方法的入参
//orderInfo.setId(2);
if (orderInfo.getId() > 3) {
throw new RuntimeException("大于3了");
}
return null;
}
};
//void方法时,只能这样写
doAnswer(demoAnswer).when(mockService).methodVoid(any(OrderInfo.class));//doAnswer:执行demoAnswer的answer方法
mockService.methodVoid(new OrderInfo() {{
setId(3);
}});
}
11. void方法的mock
doNothing().when(mockService).voidMethod(anyString(), any(Integer.class)); //什么也不做
doThrow(new RuntimeException("")).when(mockService).voidMethod(eq("ex"), eq(10001)); //抛出指定异常
doNothing().doThrow(new RuntimeException("")).when(mockService).voidMethod(anyString(), any(Integer.class)); //第一次调用什么也不做,第二次调用抛异常
相关文章
Mockito 常见操作
https://blog.csdn.net/sugelachao/article/details/124277623