一、常用的mock工具
在Java开发中,Mock工具可用于单元测试和模拟对象的创建
1、Mockito:
Mockito是Java中最受欢迎的Mocking框架之一。
提供简单易用的API,可以方便地创建和配置模拟对象。
支持验证模拟对象的行为和交互。
提供了注解支持,用于简化测试代码的编写。
文档丰富,社区活跃,广泛应用于Java项目中,SpringBootTest 默认使用的也是 Mockito
2、EasyMock:EasyMock是另一个常用的Java Mocking框架。
提供了单的API来创建和配置模拟对象。
支持验证模拟对象的行为和交互。
使用了基于录制和回放的模拟对象创建方式。
语法相对于Mockito来说稍微繁琐一些。
文档相对较少,但社区比较活跃。
3、PowerMock:PowerMock是一个扩展了Mockito和EasyMock功能的工具。
可以模拟静态方法、构造函数、私有方法等。
PowerMock的使用相对复杂,需要在测试类上使用特定的注解。
在处理一些特殊场景(如遗留代码)时非常有用。
文档相对较少,但在特定情况下仍然是一个有价值的工具。
二、spring / 非 spring 场景测试验证
选取的mock工具为 mockito ,其中依赖版本为 mockito-core:3.3.3
<dependency><groupId>org.mockito</groupId><artifactId>mockito-core</artifactId><version>3.3.3</version><scope>test</scope> </dependency>
1、场景假设,我们的希望实现用户在线时长查询的方法,其中需要校验用户是否登陆(第三方接口)
@Service
public class LoginService {@Resourceprivate SSOService ssoService;/*** 调用单点登陆系统判断* @param userId* @return*/public boolean isLogin(String userId) {UserInfo userInfo = ssoService.getLoginInfo(userId);if (Objects.nonNull(userInfo)) {return true;}return false;}
}@Service
public class UserService {@Resourceprivate LoginService loginService;/*** 获取用户在线时常* @return*/public Integer getUserOnlineTimes(String userId){//判断是否登陆if(!loginService.isLogin(userId)){return 0;}return (int)(Math.random() * 100) + 1;}
}
2、在没有SSO调用授权之前我们不好进行单元测试,这时就需要用的mock工具了
a、非 spring 方式使用mock进行测试验证,通过反射注入 mock 类
@Slf4j
@RunWith(MockitoJUnitRunner.class)
public class UserServiceTest {@Mockprivate LoginService loginServiceMock;@Testpublic void getUserOnlineTimes() throws NoSuchFieldException, IllegalAccessException {// 设置 mock 场景Mockito.when(loginServiceMock.isLogin("zhangsan")).thenReturn(false);Mockito.when(loginServiceMock.isLogin("lisi")).thenReturn(true);// 反射赋值属性 或 在 userService 作为成员变量,使用 @InjectMocks 注入UserService userService = new UserService();Field loginService = userService.getClass().getDeclaredField("loginService");loginService.setAccessible(true);loginService.set(userService, loginServiceMock);//验证Integer onlineTimes = userService.getUserOnlineTimes("zhangsan");Assert.assertEquals(Integer.valueOf(0), onlineTimes);onlineTimes = userService.getUserOnlineTimes("lisi");Assert.assertTrue(onlineTimes > 0);}
}
b、非 spring 方式使用mock进行测试验证,通过 mockito 注解 @InjectMocks 注入
@Slf4j
@RunWith(MockitoJUnitRunner.class)
public class UserServiceTest2 {@Mockprivate LoginService loginServiceMock;@InjectMocksprivate UserService userService;@Testpublic void getUserOnlineTimes() {// 设置 mock 场景Mockito.when(loginServiceMock.isLogin("zhangsan")).thenReturn(false);Mockito.when(loginServiceMock.isLogin("lisi")).thenReturn(true);//验证Integer onlineTimes = userService.getUserOnlineTimes("zhangsan");Assert.assertEquals(Integer.valueOf(0), onlineTimes);onlineTimes = userService.getUserOnlineTimes("lisi");Assert.assertTrue(onlineTimes > 0);// 验证mock方法被执行的次数Mockito.verify(loginServiceMock, Mockito.times(1)).isLogin("zhangsan");}
}
c、spring 容器中使用mock进行测试验证
@Slf4j
@SpringBootTest
@RunWith(SpringRunner.class)
public class UserServiceTestInSpring {@MockBeanprivate LoginService loginServiceMock;@Resourceprivate UserService userService;@Beforepublic void init(){// 启用mockMockitoAnnotations.initMocks(this);}@Testpublic void getUserOnlineTimes() {// 设置 mock 场景Mockito.when(loginServiceMock.isLogin("zhangsan")).thenReturn(false);Mockito.when(loginServiceMock.isLogin("lisi")).thenReturn(true);//验证Integer onlineTimes = userService.getUserOnlineTimes("zhangsan");Assert.assertEquals(Integer.valueOf(0), onlineTimes);onlineTimes = userService.getUserOnlineTimes("lisi");Assert.assertTrue(onlineTimes > 0);}
}
三、mockito 使用总结
Mockito提供了强大的验证功能,支持验证模拟对象的方法调用次数、参数和顺序,以确保代码的预期行为,另外注解的支持可以轻松地创建和注入模拟对象,让测试代码更加简化,但是Mockito本身无法直接模拟静态方法和私有方法。对于这些场景,可以使用 PowerMock 或者使用java代码直接构造生成