温馨提示:本文较长,同学们可收藏后再看 :)
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-core</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
/**
* 用户服务类
*/
public class UserService {
/** 服务相关 */
/** 用户DAO */
private UserDAO userDAO;
/** 标识生成器 */
private IdGenerator idGenerator;
/** 参数相关 */
/** 可以修改 */
"${userService.canModify}") (
private Boolean canModify;
/**
* 创建用户
*
* @param userCreate 用户创建
* @return 用户标识
*/
public Long createUser(UserVO userCreate) {
// 获取用户标识
Long userId = userDAO.getIdByName(userCreate.getName());
// 根据存在处理
// 根据存在处理: 不存在则创建
if (Objects.isNull(userId)) {
userId = idGenerator.next();
UserDO create = new UserDO();
create.setId(userId);
create.setName(userCreate.getName());
userDAO.create(create);
}
// 根据存在处理: 已存在可修改
else if (Boolean.TRUE.equals(canModify)) {
UserDO modify = new UserDO();
modify.setId(userId);
modify.setName(userCreate.getName());
userDAO.modify(modify);
}
// 根据存在处理: 已存在禁修改
else {
throw new UnsupportedOperationException("不支持修改");
}
// 返回用户标识
return userId;
}
}
/**
* 用户服务测试类
*/
(PowerMockRunner.class)
public class UserServiceTest {
/** 模拟依赖对象 */
/** 用户DAO */
private UserDAO userDAO;
/** 标识生成器 */
private IdGenerator idGenerator;
/** 定义被测对象 */
/** 用户服务 */
private UserService userService;
/**
* 在测试之前
*/
public void beforeTest() {
// 注入依赖对象
Whitebox.setInternalState(userService, "canModify", Boolean.TRUE);
}
/**
* 测试: 创建用户-新
*/
public void testCreateUserWithNew() {
// 模拟依赖方法
// 模拟依赖方法: userDAO.getByName
Mockito.doReturn(null).when(userDAO).getIdByName(Mockito.anyString());
// 模拟依赖方法: idGenerator.next
Long userId = 1L;
Mockito.doReturn(userId).when(idGenerator).next();
// 调用被测方法
String text = ResourceHelper.getResourceAsString(getClass(), "userCreateVO.json");
UserVO userCreate = JSON.parseObject(text, UserVO.class);
Assert.assertEquals("用户标识不一致", userId, userService.createUser(userCreate));
// 验证依赖方法
// 验证依赖方法: userDAO.getByName
Mockito.verify(userDAO).getIdByName(userCreate.getName());
// 验证依赖方法: idGenerator.next
Mockito.verify(idGenerator).next();
// 验证依赖方法: userDAO.create
ArgumentCaptor<UserDO> userCreateCaptor = ArgumentCaptor.forClass(UserDO.class);
Mockito.verify(userDAO).create(userCreateCaptor.capture());
text = ResourceHelper.getResourceAsString(getClass(), "userCreateDO.json");
Assert.assertEquals("用户创建不一致", text, JSON.toJSONString(userCreateCaptor.getValue()));
// 验证依赖对象
Mockito.verifyNoMoreInteractions(idGenerator, userDAO);
}
/**
* 测试: 创建用户-旧
*/
public void testCreateUserWithOld() {
// 模拟依赖方法
// 模拟依赖方法: userDAO.getByName
Long userId = 1L;
Mockito.doReturn(userId).when(userDAO).getIdByName(Mockito.anyString());
// 调用被测方法
String text = ResourceHelper.getResourceAsString(getClass(), "userCreateVO.json");
UserVO userCreate = JSON.parseObject(text, UserVO.class);
Assert.assertEquals("用户标识不一致", userId, userService.createUser(userCreate));
// 验证依赖方法
// 验证依赖方法: userDAO.getByName
Mockito.verify(userDAO).getIdByName(userCreate.getName());
// 验证依赖方法: userDAO.modify
ArgumentCaptor<UserDO> userModifyCaptor = ArgumentCaptor.forClass(UserDO.class);
Mockito.verify(userDAO).modify(userModifyCaptor.capture());
text = ResourceHelper.getResourceAsString(getClass(), "userModifyDO.json");
Assert.assertEquals("用户修改不一致", text, JSON.toJSONString(userModifyCaptor.getValue()));
// 验证依赖对象
Mockito.verifyNoInteractions(idGenerator);
Mockito.verifyNoMoreInteractions(userDAO);
}
/**
* 测试: 创建用户-异常
*/
public void testCreateUserWithException() {
// 注入依赖对象
Whitebox.setInternalState(userService, "canModify", Boolean.FALSE);
// 模拟依赖方法
// 模拟依赖方法: userDAO.getByName
Long userId = 1L;
Mockito.doReturn(userId).when(userDAO).getIdByName(Mockito.anyString());
// 调用被测方法
String text = ResourceHelper.getResourceAsString(getClass(), "userCreateVO.json");
UserVO userCreate = JSON.parseObject(text, UserVO.class);
UnsupportedOperationException exception = Assert.assertThrows("返回异常不一致",
UnsupportedOperationException.class, () -> userService.createUser(userCreate));
Assert.assertEquals("异常消息不一致", "不支持修改", exception.getMessage());
}
}
{"name":"test"}
{"id":1,"name":"test"}
{"id":1,"name":"test"}
/** 定义被测对象 */
/** 用户服务 */
@InjectMocks
private UserService userService;
/** 模拟依赖对象 */
/** 用户DAO */
@Mock
private UserDAO userDAO;
/** 标识生成器 */
@Mock
private IdGenerator idGenerator;
/** 定义被测对象 */
/** 用户服务 */
private UserService userService;
/**
* 在测试之前
*/
public void beforeTest() {
// 注入依赖对象
Whitebox.setInternalState(userService, "canModify", Boolean.TRUE);
}
Long userId = 1L;
// 模拟依赖方法
// 模拟依赖方法: userDAO.getByName
Mockito.doReturn(null).when(userDAO).getIdByName(Mockito.anyString());
// 模拟依赖方法: idGenerator.next
Mockito.doReturn(userId).when(idGenerator).next();
String text = ResourceHelper.getResourceAsString(getClass(), "userCreateVO.json");
UserVO userCreate = JSON.parseObject(text, UserVO.class);
userService.createUser(userCreate)
Assert.assertEquals("用户标识不一致", userId, userService.createUser(userCreate));
// 验证依赖方法
// 验证依赖方法: userDAO.getByName
Mockito.verify(userDAO).getIdByName(userCreate.getName());
// 验证依赖方法: idGenerator.next
Mockito.verify(idGenerator).next();
// 验证依赖方法: userDAO.create
ArgumentCaptor userCreateCaptor = ArgumentCaptor.forClass(UserDO.class);
Mockito.verify(userDAO).create(userCreateCaptor.capture());
text = ResourceHelper.getResourceAsString(getClass(), "userCreateDO.json");
Assert.assertEquals("用户创建不一致", text, JSON.toJSONString(userCreateCaptor.getValue()));
// 验证依赖对象
Mockito.verifyNoMoreInteractions(idGenerator, userDAO);
UserService userService = new UserService();
UserService userService = Mockito.spy(new UserService());
UserService userService = Mockito.spy(UserService.class);
AbstractOssService ossService = Mockito.spy(AbstractOssService.class);
(PowerMockRunner.class)
public class CompanyServiceTest {
private UserService userService = new UserService();
...
}
注意:@Spy注解对象需要初始化。如果是虚基类或接口,可以用Mockito.mock方法实例化。
public class UserServiceTest {
private UserService userService;
...
}
Long userId = 1L;
String userName = "admin";
UserDO user = new User();
user.setId(userId);
user.setName(userName);
List userIdList = Arrays.asList(1L, 2L, 3L);
String text = ResourceHelper.getResourceAsString(getClass(), "user.json");
UserDO user = JSON.parseObject(text, UserDO.class);
String text = ResourceHelper.getResourceAsString(getClass(), "userList.json");
List userList = JSON.parseArray(text, UserDO.class);
String text = ResourceHelper.getResourceAsString(getClass(), "userMap.json");
Map userMap = JSON.parseObject(text, new TypeReference>() {});
只使用类实例,不使用类属性;
类属性太多,但使用其中少量属性(可以mock属性返回值);
类是接口或虚基类,并不关心其具体实现类。
MockClass mockClass = Mockito.mock(MockClass.class);
List userIdList = (List)Mockito.mock(List.class);
public class UserServiceTest {
private UserDAO userDAO;
...
}
UserService userService = Mockito.spy(new UserService());
UserService userService = Mockito.spy(UserService.class);
AbstractOssService ossService = Mockito.spy(AbstractOssService.class);
(PowerMockRunner.class)
public class CompanyServiceTest {
private UserService userService = new UserService();
...
}
注意:@Spy注解对象需要初始化。如果是虚基类或接口,可以用Mockito.mock方法实例化。
userService.setMaxCount(100);
userService.setUserDAO(userDAO);
ReflectionTestUtils.setField(userService, "maxCount", 100);
ReflectionTestUtils.setField(userService, "userDAO", userDAO);
Whitebox.setInternalState(userService, "maxCount", 100);
Whitebox.setInternalState(userService, "userDAO", userDAO);
public class UserServiceTest {
private UserDAO userDAO;
private Boolean canModify;
private UserService userService;
...
}
public final class FieldHelper {
public static void setStaticFinalField(Class clazz, String fieldName, Object fieldValue) throws NoSuchFieldException, IllegalAccessException {
Field field = clazz.getDeclaredField(fieldName);
FieldUtils.removeFinalModifier(field);
FieldUtils.writeStaticField(field, fieldValue, true);
}
}
FieldHelper.setStaticFinalField(UserService.class, "log", log);
注意:经过测试,该方法对于int、Integer等基础类型并不生效,应该是编译器常量优化导致。
Mockito.doNothing().when(userDAO).delete(userId);
Mockito.doReturn(user).when(userDAO).get(userId);
Mockito.when(userDAO.get(userId)).thenReturn(user);
Mockito.doReturn(record0, record1, record2, null).when(recordReader).read();
Mockito.when(recordReader.read()).thenReturn(record0, record1, record2, null);
List recordList = ...;
Mockito.doReturn(recordList.get(0), recordList.subList(1, recordList.size()).toArray()).when(recordReader).read();
Mockito.when(recordReader.read()).thenReturn(recordList.get(0), recordList.subList(1, recordList.size()).toArray());
Map userMap = ...;
Mockito.doAnswer(invocation -> userMap.get(invocation.getArgument(0)))
.when(userDAO).get(Mockito.anyLong());
Mockito.when(userDAO.get(Mockito.anyLong()))
.thenReturn(invocation -> userMap.get(invocation.getArgument(0)));
Mockito.when(userDAO.get(Mockito.anyLong()))
.then(invocation -> userMap.get(invocation.getArgument(0)));
Mockito.doThrow(PersistenceException.class).when(userDAO).get(Mockito.anyLong());
Mockito.when(userDAO.get(Mockito.anyLong())).thenThrow(PersistenceException.class);
Mockito.doThrow(exception).when(userDAO).get(Mockito.anyLong());
Mockito.when(userDAO.get(Mockito.anyLong())).thenThrow(exception);
Mockito.doThrow(PersistenceException.class, RuntimeException.class).when(userDAO).get(Mockito.anyLong());
Mockito.when(userDAO.get(Mockito.anyLong())).thenThrow(PersistenceException.class, RuntimeException.class);
Mockito.doThrow(exception1, exception2).when(userDAO).get(Mockito.anyLong());
Mockito.when(userDAO.get(Mockito.anyLong())).thenThrow(exception1, exception2);
Mockito.doCallRealMethod().when(userService).getUser(userId);
Mockito.when(userService.getUser(userId)).thenCallRealMethod();
Mockito.doReturn(deleteCount).when(userDAO).deleteAll();
Mockito.when(userDAO.deleteAll()).thenReturn(deleteCount);
Mockito.doReturn(user).when(userDAO).get(userId);
Mockito.when(userDAO.get(userId)).thenReturn(user);
Mockito.doReturn(user).when(userDAO).get(Mockito.anyLong());
Mockito.when(userDAO.get(Mockito.anyLong())).thenReturn(user);
Mockito.doReturn(user).when(userDAO)
.queryCompany(Mockito.anyLong(), Mockito.nullable(Long.class));
Mockito.when(userDAO.queryCompany(Mockito.anyLong(), Mockito.<Long>any()))
.thenReturn(user);
Mockito.doReturn(user).when(userDAO).queryCompany(Mockito.anyLong(), Mockito.isNull());
Mockito.when(userDAO.queryCompany(Mockito.anyLong(), Mockito.eq(null))).thenReturn(user);
Mockito.doReturn(user1).when(userDAO).get(1L);
Mockito.doReturn(user2).when(userDAO).get(2L);
...
注意:如果一个参数满足多个模拟方法条件,会以最后一个模拟方法为准。
Mockito.when(userService.delete(Mockito.anyLong()).thenReturn(true);
Mockito.when(userService.delete(1L, 2L, 3L).thenReturn(true);
Mockito.when(userService.delete(Mockito.<Long>any()).thenReturn(true);
注意:Mockito.any()并不等于Mockito.any(Class type),前者可以匹配null和类型T的可变参数,后者只能匹配T必填参数。
// 添加@PrepareForTest注解
// 跟模拟普通方法完全一致
Mockito.doReturn(userId).when(idGenerator).next();
Mockito.when(idGenerator.next()).thenReturn(userId);
PowerMockito.doReturn(true).when(UserService.class, "isSuper", userId);
PowerMockito.when(UserService.class, "isSuper", userId).thenReturn(true);
PowerMockito.whenNew(UserDO.class).withNoArguments().thenReturn(userDO);
PowerMockito.whenNew(UserDO.class).withArguments(userId, userName).thenReturn(userDO);
// 模拟对应的类
PowerMockito.mockStatic(HttpHelper.class);
PowerMockito.spy(HttpHelper.class);
// 模拟对应的方法
PowerMockito.when(HttpHelper.httpPost(SERVER_URL)).thenReturn(response);
PowerMockito.doReturn(response).when(HttpHelper.class, "httpPost", SERVER_URL);
PowerMockito.when(HttpHelper.class, "httpPost", SERVER_URL).thenReturn(response);
注意:第一种方式不适用于PowerMockito.spy模拟的静态方法类。
修饰符 | 本类 | 本包 | 子类 | 其它 |
public | 有 | 有 | 有 | 有 |
protected | 有 | 有 | 有 | 无 |
缺省 | 有 | 有 | 无 | 无 |
private | 有 | 无 | 无 | 无 |
UserDO user = new User();
UserDO user = new User(1L, "admin");
Whitebox.invokeConstructor(NumberHelper.class);
Whitebox.invokeConstructor(User.class, 1L, "admin");
备注:该方法也可以调用有访问权限的构造方法,但是不建议使用。
userService.deleteUser(userId);
User user = userService.getUser(userId);
User user = (User)Whitebox.invokeMethod(userService, "isSuper", userId);
Method method = Whitebox.getMethod(UserService.class, "isSuper", Long.class);
Method method = PowerMockito.method(UserService.class, "isSuper", Long.class);
User user = (User)method.invoke(userService, userId);
备注:该方法也可以调用有访问权限的普通方法,但是不建议使用。
boolean isPositive = NumberHelper.isPositive(-1);
String value = (String)Whitebox.invokeMethod(JSON.class, "toJSONString", object);
备注:该方法也可以调用有访问权限的静态方法,但是不建议使用。
Mockito.verify(userDAO).deleteAll();
Mockito.verify(userDAO).delete(userId);
Mockito.verify(userDAO).delete(Mockito.eq(userId));
Mockito.verify(userDAO).delete(Mockito.anyLong());
Mockito.verify(userDAO).queryCompany(Mockito.anyLong(), Mockito.nullable(Long.class));
Mockito.verify(userDAO).queryCompany(Mockito.anyLong(), Mockito.isNull());
Mockito.verify(userService).delete(Mockito.any(Long.class));
Mockito.verify(userService).delete(1L, 2L, 3L);
Mockito.verify(userService).delete(Mockito.<Long>any());
Mockito.verify(userDAO).delete(userId);
Mockito.verify(userDAO, Mockito.never()).delete(userId);
Mockito.verify(userDAO, Mockito.times(n)).delete(userId);
Mockito.verify(userDAO, Mockito.atLeastOnce()).delete(userId);
Mockito.verify(userDAO, Mockito.atLeast(n)).delete(userId);
Mockito.verify(userDAO, Mockito.atMostOnce()).delete(userId);
Mockito.verify(userDAO, Mockito.atMost(n)).delete(userId);
Mockito.verify(userDAO, Mockito.call(n)).delete(userId);
Mockito.verify(userDAO, Mockito.only()).delete(userId);
Mockito.verify(userDAO).delete(userId);
Mockito.verifyNoMoreInteractions(userDAO);
capture方法,用于捕获方法参数;
getValue方法,用于获取捕获的参数值,如果捕获了多个参数值,该方法只返回最后一个参数值;
getAllValues方法,用户获取捕获的所有参数值。
ArgumentCaptor<UserDO> userCaptor = ArgumentCaptor.forClass(UserDO.class);
Mockito.verify(userDAO).modify(userCaptor.capture());
UserDO user = userCaptor.getValue();
注意:定义泛型类的参数捕获器时,存在强制类型转换,会引起编译器警告。
(PowerMockRunner.class)
public class UserServiceTest {
private ArgumentCaptor<UserDO> userCaptor;
public void testModifyUser() {
...
Mockito.verify(userDAO).modify(userCaptor.capture());
UserDO user = userCaptor.getValue();
}
}
注意:定义泛型类的参数捕获器时,由于是Mockito自行初始化,不会引起编译器警告。
ArgumentCaptor<UserDO> userCaptor = ArgumentCaptor.forClass(UserDO.class);
Mockito.verify(userDAO, Mockito.atLeastOnce()).modify(userCaptor.capture());
List<UserDO> userList = userCaptor.getAllValues();
PowerMockito.verifyPrivate(myClass, times(1)).invoke("unload", any(List.class));
PowerMockito.verifyNew(MockClass.class).withNoArguments();
PowerMockito.verifyNew(MockClass.class).withArguments(someArgs);
PowerMockito.verifyStatic(StringUtils.class);
StringUtils.isEmpty(string);
Assert.assertNull("用户标识必须为空", userId);
Assert.assertNotNull("用户标识不能为空", userId);
Assert.assertTrue("返回值必须为真", NumberHelper.isPositive(1));
Assert.assertFalse("返回值必须为假", NumberHelper.isPositive(-1));
UserDO expectedUser = ...;
Mockito.doReturn(expectedUser).when(userDAO).get(userId);
UserDO actualUser = userService.getUser(userId);
Assert.assertSame("用户必须一致", expectedUser, actualUser);
UserDO expectedUser = ...;
Mockito.doReturn(expectedUser).when(userDAO).get(userId);
UserDO actualUser = userService.getUser(otherUserId);
Assert.assertNotSame("用户不能一致", expectedUser, actualUser);
Assert.assertNotEquals("用户名称不一致", "admin", userName);
Assert.assertEquals("账户金额不一致", 10000.0D, accountAmount, 1E-6D);
Long[] userIds = ...;
Assert.assertArrayEquals("用户标识列表不一致", new Long[] {1L, 2L, 3L}, userIds);
List<Long> userIdList = ...;
Assert.assertEquals("用户标识列表不一致", Arrays.asList(1L, 2L, 3L), userIdList);
UserDO user = ...;
Assert.assertEquals("用户标识不一致", Long.valueOf(1L), user.getId());
Assert.assertEquals("用户名称不一致", "admin", user.getName());
Assert.assertEquals("用户公司标识不一致", Long.valueOf(1L), user.getCompany().getId());
...
List<UserDO> expectedUserList = ...;
List<UserDO> actualUserList = ...;
Assert.assertEquals("用户列表长度不一致", expectedUserList.size(), actualUserList.size());
UserDO[] expectedUsers = expectedUserList.toArray(new UserDO[0]);
UserDO[] actualUsers = actualUserList.toArray(new UserDO[0]);
for (int i = 0; i < actualUsers.length; i++) {
Assert.assertEquals(String.format("用户(%s)标识不一致", i), expectedUsers[i].getId(), actualUsers[i].getId());
Assert.assertEquals(String.format("用户(%s)名称不一致", i), expectedUsers[i].getName(), actualUsers[i].getName());
Assert.assertEquals("用户公司标识不一致", expectedUsers[i].getCompany().getId(), actualUsers[i].getCompany().getId());
...
}
List<UserDO> userList = ...;
String text = ResourceHelper.getResourceAsString(getClass(), "userList.json");
Assert.assertEquals("用户列表不一致", text, JSON.toJSONString(userList));
Map<Long, Map<String, Object>> userMap = ...;
String text = ResourceHelper.getResourceAsString(getClass(), "userMap.json");
Assert.assertEquals("用户映射不一致", text, JSON.toJSONString(userMap, SerializerFeature.MapSortField));
MapperScannerConfigurer configurer = myBatisConfiguration.buildMapperScannerConfigurer();
Assert.assertEquals("基础包不一致", "com.alibaba.example", Whitebox.getInternalState(configurer, "basePackage"));
public void testGetUser() {
// 模拟依赖方法
Mockito.doReturn(null).when(userDAO).get(userId);
// 调用被测方法
userService.getUser(userId);
}
private ExpectedException exception = ExpectedException.none();
public void testGetUser() {
// 模拟依赖方法
Long userId = 123L;
Mockito.doReturn(null).when(userDAO).get(userId);
// 调用被测方法
exception.expect(ExampleException.class);
exception.expectMessage(String.format("用户(%s)不存在", userId));
userService.getUser(userId);
}
public void testGetUser() {
// 模拟依赖方法
Long userId = 123L;
Mockito.doReturn(null).when(userDAO).get(userId);
// 调用被测方法
ExampleException exception = Assert.assertThrows("异常类型不一致", ExampleException.class, () -> userService.getUser(userId));
Assert.assertEquals("异常消息不一致", "处理异常", exception.getMessage());
}
Mockito.verifyNoInteractions(idGenerator, userDAO);
Mockito.verifyNoMoreInteractions(idGenerator, userDAO);
备注:Mockito的verifyZeroInteractions方法与verifyNoMoreInteractions方法功能相同,但是目前前者已经被废弃。
// 清除所有对象调用
Mockito.clearInvocations();
// 清除指定对象调用
Mockito.clearInvocations(idGenerator, userDAO);
Mockito不支持对静态方法、构造方法、final方法、私有方法的模拟;
Mockito的any相关的参数匹配方法并不支持可空参数和空参数;
采用Mockito的参数匹配方法时,其它参数不能直接用常量或变量,必须使用Mockito的eq方法;
使用when-then语句模拟Spy对象方法会先执行真实方法,应该使用do-when语句;
PowerMock对静态方法、构造方法、final方法、私有方法的模拟需要把对应的类添加到@PrepareForTest注解中;
PowerMock模拟JDK的静态方法、构造方法、final方法、私有方法时,需要把使用这些方法的类加入到@PrepareForTest注解中,从而导致单元测试覆盖率不被统计;
PowerMock使用自定义的类加载器来加载类,可能导致系统类加载器认为有类型转换问题;需要加上@PowerMockIgnore({“javax.crypto.*”})注解,来告诉PowerMock这个包不要用PowerMock的类加载器加载,需要采用系统类加载器来加载。
……
public <T> void readData(RecordReader recordReader, int batchSize, Function<Record, T> dataParser, Predicate<List<T>> dataStorage) {
try {
// 依次读取数据
Record record;
boolean isContinue = true;
List<T> dataList = new ArrayList<>(batchSize);
while (Objects.nonNull(record = recordReader.read()) && isContinue) {
// 解析添加数据
T data = dataParser.apply(record);
if (Objects.nonNull(data)) {
dataList.add(data);
}
// 批量存储数据
if (dataList.size() == batchSize) {
isContinue = dataStorage.test(dataList);
dataList.clear();
}
}
// 存储剩余数据
if (CollectionUtils.isNotEmpty(dataList)) {
dataStorage.test(dataList);
dataList.clear();
}
} catch (IOException e) {
String message = READ_DATA_EXCEPTION;
log.warn(message, e);
throw new ExampleException(message, e);
}
}
public void testReadData() throws Exception {
// 模拟依赖方法
// 模拟依赖方法: recordReader.read
Record record0 = Mockito.mock(Record.class);
Record record1 = Mockito.mock(Record.class);
Record record2 = Mockito.mock(Record.class);
TunnelRecordReader recordReader = Mockito.mock(TunnelRecordReader.class);
Mockito.doReturn(record0, record1, record2, null).when(recordReader).read();
// 模拟依赖方法: dataParser.apply
Object object0 = new Object();
Object object1 = new Object();
Object object2 = new Object();
Function<Record, Object> dataParser = Mockito.mock(Function.class);
Mockito.doReturn(object0).when(dataParser).apply(record0);
Mockito.doReturn(object1).when(dataParser).apply(record1);
Mockito.doReturn(object2).when(dataParser).apply(record2);
// 模拟依赖方法: dataStorage.test
Predicate<List<Object>> dataStorage = Mockito.mock(Predicate.class);
Mockito.doReturn(true).when(dataStorage).test(Mockito.anyList());
// 调用测试方法
odpsService.readData(recordReader, 2, dataParser, dataStorage);
// 验证依赖方法
// 模拟依赖方法: recordReader.read
Mockito.verify(recordReader, Mockito.times(4)).read();
// 模拟依赖方法: dataParser.apply
Mockito.verify(dataParser, Mockito.times(3)).apply(Mockito.any(Record.class));
// 验证依赖方法: dataStorage.test
ArgumentCaptor<List<Object>> recordListCaptor = ArgumentCaptor.forClass(List.class);
Mockito.verify(dataStorage, Mockito.times(2)).test(recordListCaptor.capture());
Assert.assertEquals("数据列表不一致", Arrays.asList(Arrays.asList(object0, object1), Arrays.asList(object2)), recordListCaptor.getAllValues());
}
java.lang.AssertionError: 数据列表不一致 expected:<[[java.lang.Object@1e3469df, java.lang.Object@79499fa], [java.lang.Object@48531d5]]> but was:<[[], []]>
public void testReadData() throws Exception {
// 模拟依赖方法
...
// 模拟依赖方法: dataStorage.test
List<Object> dataList = new ArrayList<>();
Predicate<List<Object>> dataStorage = Mockito.mock(Predicate.class);
Mockito.doAnswer(invocation -> dataList.addAll((List<Object>)invocation.getArgument(0)))
.when(dataStorage).test(Mockito.anyList());
// 调用测试方法
odpsService.readData(recordReader, 2, dataParser, dataStorage);
// 验证依赖方法
...
// 验证依赖方法: dataStorage.test
Mockito.verify(dataStorage, Mockito.times(2)).test(Mockito.anyList());
Assert.assertEquals("数据列表不一致", Arrays.asList(object0, object1, object2), dataList);
}
@Slf4j
@Service
public class ExampleService {
public void recordLog(int code) {
if (code == 1) {
log.info("执行分支1");
return;
}
if (code == 2) {
log.info("执行分支2");
return;
}
log.info("执行默认分支");
}
...
}
@RunWith(PowerMockRunner.class)
public class ExampleServiceTest {
@Mock
private Logger log;
@InjectMocks
private ExampleService exampleService;
@Test
public void testRecordLog1() {
exampleService.recordLog(1);
Mockito.verify(log).info("执行分支1");
}
}
Wanted but not invoked:
logger.info("执行分支1");
(PowerMockRunner.class)
public class ExampleServiceTest {
private Logger log;
private ExampleService exampleService;
public void beforeTest() throws Exception {
FieldHelper.setStaticFinalField(ExampleService.class, "log", log);
}
public void testRecordLog1() {
exampleService.recordLog(1);
Mockito.verify(log).info("执行分支1");
}
}
public class MetaqMessageSender {
private MetaProducer metaProducer;
public String sendMetaqMessage(String topicName, String tagName, String messageKey, String messageBody) {
try {
// 组装消息内容
Message message = new Message();
message.setTopic(topicName);
message.setTags(tagName);
message.setKeys(messageKey);
message.setBody(messageBody.getBytes(StandardCharsets.UTF_8));
// 发送消息请求
SendResult sendResult = metaProducer.send(message);
if (sendResult.getSendStatus() != SendStatus.SEND_OK) {
String msg = String.format("发送标签(%s)消息(%s)状态错误(%s)", tagName, messageKey, sendResult.getSendStatus());
log.warn(msg);
throw new ReconsException(msg);
}
log.info(String.format("发送标签(%s)消息(%s)状态成功:%s", tagName, messageKey, sendResult.getMsgId()));
// 返回消息标识
return sendResult.getMsgId();
} catch (MQClientException | RemotingException | MQBrokerException | InterruptedException e) {
// 记录消息异常
Thread.currentThread().interrupt();
String message = String.format("发送标签(%s)消息(%s)状态异常:%s", tagName, messageKey, e.getMessage());
log.warn(message, e);
throw new ReconsException(message, e);
}
}
}
(PowerMockRunner.class)
public class MetaqMessageSenderTest {
private MetaProducer metaProducer;
private MetaqMessageSender metaqMessageSender;
public void testSendMetaqMessage() throws Exception {
// 模拟依赖方法
SendResult sendResult = new SendResult();
sendResult.setMsgId("msgId");
sendResult.setSendStatus(SendStatus.SEND_OK);
Mockito.doReturn(sendResult).when(metaProducer).send(Mockito.any(Message.class));
// 调用测试方法
String topicName = "topicName";
String tagName = "tagName";
String messageKey = "messageKey";
String messageBody = "messageBody";
String messageId = metaqMessageSender.sendMetaqMessage(topicName, tagName, messageKey, messageBody);
Assert.assertEquals("messageId不一致", sendResult.getMsgId(), messageId);
// 验证依赖方法
ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
Mockito.verify(metaProducer).send(messageCaptor.capture());
Message message = messageCaptor.getValue();
Assert.assertEquals("topicName不一致", topicName, message.getTopic());
Assert.assertEquals("tagName不一致", tagName, message.getTags());
Assert.assertEquals("messageKey不一致", messageKey, message.getKeys());
Assert.assertEquals("messageBody不一致", messageBody, new String(message.getBody()));
}
}
java.lang.RuntimeException: com.alibaba.rocketmq.client.producer.SendResult was loaded by org.powermock.core.classloader.javassist.JavassistMockClassLoader@5d43661b, it should be loaded by Pandora Container. Can not load this fake sdk class.
(PandoraBootRunner.class)
public class MetaqMessageSenderTest {
...
public void beforeTest() {
MockitoAnnotations.initMocks(this);
}
...
}
Type safety: Unchecked cast from Object to List<Object>
Type safety: Unchecked invocation forClass(Class<Map>) of the generic method forClass(Class<S>) of type ArgumentCaptor
Type safety: The expression of type ArgumentCaptor needs unchecked conversion to conform to ArgumentCaptor<Map<String,Object>>
Map<Long, String> resultMap = Mockito.mock(Map.class);
ArgumentCaptor<Map<String, Object>> parameterMapCaptor = ArgumentCaptor.forClass(Map.class);
private Map<Long, String> resultMap;
private ArgumentCaptor<Map<String, Object>> parameterMapCaptor;
Function<Record, Object> dataParser = Mockito.mock(Function.class);
AbstractDynamicValue<Long, Integer> dynamicValue = Mockito.spy(AbstractDynamicValue.class);
ArgumentCaptor<ActionRequest<Void>> requestCaptor = ArgumentCaptor.forClass(ActionRequest.class);
/** 定义临时类或接口 */
private interface DataParser extends Function<Record, Object> {};
private static abstract class AbstractTemporaryDynamicValue extends AbstractDynamicValue<Long, Integer> {};
private static class VoidActionRequest extends ActionRequest<Void> {};
/** 使用临时类或接口 */
Function<Record, Object> dataParser = Mockito.mock(DataParser.class);
AbstractDynamicValue<Long, Integer> dynamicValue = Mockito.spy(AbstractTemporaryDynamicValue.class);
ArgumentCaptor<ActionRequest<Void>> requestCaptor = ArgumentCaptor.forClass(VoidActionRequest.class);
Function<Record, Object> dataParser = Mockito.mock(Function.class);
ArgumentCaptor<ActionRequest<Void>> requestCaptor = ArgumentCaptor.forClass(ActionRequest.class);
Map<Long, Double> scoreMap = (Map<Long, Double>)method.invoke(userService);
Function<Record, Object> dataParser = CastUtils.cast(Mockito.mock(Function.class));
ArgumentCaptor<ActionRequest<Void>> requestCaptor = CastUtils.cast(ArgumentCaptor.forClass(ActionRequest.class));
Map<Long, Double> scoreMap = CastUtils.cast(method.invoke(userService));
注意:CastUtils.cast方法本质是——先转换为Object类型,再强制转换对应类型,本身不会对类型进行校验。所以,CastUtils.cast方法好用,但是不要乱用,否则就是大坑(只有执行时才能发现问题)。
<T> T getArgument(int index);
public static <T> T any();
public static synchronized <T> T invokeMethod(Object instance, String methodToExecute, Object... arguments) throws Exception;
Mockito.doAnswer(invocation -> dataList.addAll((List<Object>)invocation.getArgument(0)))
.when(dataStorage).test(Mockito.anyList());
Mockito.doThrow(e).when(workflow).beginToPrepare((ActionRequest<Void>)Mockito.any());
Map<Long, Double> scoreMap = (Map<Long, Double>)Whitebox.invokeMethod(userService, "getScoreMap");
Mockito.doAnswer(invocation -> dataList.addAll(invocation.getArgument(0)))
.when(dataStorage).test(Mockito.anyList());
Mockito.doThrow(e).when(workflow).beginToPrepare(Mockito.any());
Map<Long, Double> scoreMap = Whitebox.invokeMethod(userService, "getScoreMap");
List<String> valueList = Mockito.mock(List.class);
Mockito.when(listOperations.range(KEY, start, end)).thenReturn(valueList);
List > valueList = Mockito.mock(List.class);
Mockito.doReturn(valueList).when(listOperations).range(KEY, start, end);
Method method = PowerMockito.method(UserService.class, "getScoreMap");
Map<Long, Double> scoreMap = (Map<Long, Double>)method.invokeMethod(userService);
Map<Long, Double> scoreMap = Whitebox.invokeMethod(userService, "getScoreMap");
JSONArray jsonArray = (JSONArray)object;
...
if (object instanceof JSONArray) {
JSONArray jsonArray = (JSONArray)object;
...
}
public static <V> V parseValue(String text, Class<V> clazz) {
if (Objects.equals(clazz, String.class)) {
return (V)text;
}
return JSON.parseObject(text, clazz);
}
public static <V> V parseValue(String text, Class<V> clazz) {
if (Objects.equals(clazz, String.class)) {
return clazz.cast(text);
}
return JSON.parseObject(text, clazz);
}
Boolean isSupper = (Boolean)method.invokeMethod(userService, userId);
Assert.assertEquals("期望值不为真", Boolean.TRUE, isSupper);
List<UserVO> userList = (Map<Long, Double>)method.invokeMethod(userService, companyId);
Assert.assertEquals("期望值不一致", expectedJson, JSON.toJSONString(userList));
Assert.assertEquals("期望值不为真", Boolean.TRUE, method.invokeMethod(userService, userId));
Object userList = method.invokeMethod(userService, companyId);
Assert.assertEquals("期望值不一致", expectedJson, JSON.toJSONString(userList));
登妙峰山记
山高路远车难骑,
精疲力尽人易弃。
多少妙峰登顶者,
又练心境又练力!
免费领取电子书
《〈Java开发手册(泰山版)〉灵魂13问》
《Java开发手册(泰山版)》新增了5条日期时间规约、新增2条表别名sql规约以及新增统一错误码规约。为了帮助同学们更好的理解这些规约背后的原理,本书作者结合自身开发时所遇到的问题,深度剖析Java规约背后的原理,是《Java开发手册》必备的伴读书目!