Mockito (二十四)
Mockito项目实战demo(怎么用mock代替本类方法调用,即this调用)
情形一
@Service @Transactional @RequiredArgsConstructor(onConstructor = @__(@Autowired)) public class CommentServiceImpl extends ServiceImpl<CommentMapper, Comment> implements ICommentService { private final ICommentService commentService; private final IHistoryService historyService; // 构造方法注入,单测时就可以注入mock对象,不然需要提供set方法注入mock对象 public CommentServiceImpl(ICommentService commentService, IHistoryService historyService) { this.commentService = commentService; this.historyService = historyService; } @Override public MobileResponse commentDelete(Long id, Long userId) { //查询原留言 Comment originalComment = commentService.selectById(id); //本来是this调用,改为实例对象调用,方便mock //查询是否是自己的评论 if (originalComment.getCreateUserId().equals(userId.intValue())) { boolean result = commentService.deleteById(id);//本来是this调用,改为实例对象调用,方便mock History history = new History(userId.intValue(), userId.intValue(), OperationTypeEnum.DELETE_COMMENT.getValue(), originalComment.getContent(), "-", originalComment.getInfoId()); result = historyService.insert(history); return MobileResponse.success(result); } return MobileResponse.error(MobileError.CAN_ONLY_DELETE_THEIR_OWN); } }
package com.stylefeng.guns.modular.detailRules.comment.service.impl; import cn.hutool.core.lang.Assert; import com.stylefeng.guns.core.base.protocol.MobileResponse; import com.stylefeng.guns.modular.detailRules.comment.model.Comment; import com.stylefeng.guns.modular.detailRules.comment.service.ICommentService; import com.stylefeng.guns.modular.detailRules.history.model.History; import com.stylefeng.guns.modular.detailRules.history.model.enums.OperationTypeEnum; import com.stylefeng.guns.modular.detailRules.history.service.IHistoryService; import org.junit.Test; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class CommentServiceImplTest { /** * 该测试方法模拟用户有删除权限的场景; * * mock对象调用方法的参数类型和值内容必须完全一致,才能得到mock预计的返回值; * 比如这个:when(commentService.selectById(id)).thenReturn(originalComment); * 如果id为int型就不会触发该预计的返回,或者实际传递的id为10,该when中传递的id为9,内容不一致,也不会触发该预计的返回; * @param * @return void */ @Test public void commentDelete() { /** * 构造mock对象 */ Comment originalComment = mock(Comment.class); when(originalComment.getCreateUserId()).thenReturn(130); ICommentService commentService = mock(ICommentService.class); long id = 10; when(commentService.selectById(id)).thenReturn(originalComment); when(commentService.deleteById(id)).thenReturn(true); IHistoryService historyService = mock(IHistoryService.class); Long userId = 130L; History history = new History(userId.intValue(), userId.intValue(), OperationTypeEnum.DELETE_COMMENT.getValue(), originalComment.getContent(), "-", originalComment.getInfoId()); when(historyService.insert(history)).thenReturn(true); /** * 构造真实对象,调用方法 */ ICommentService commentService1 = new CommentServiceImpl(commentService, historyService); MobileResponse mobileResponse = commentService1.commentDelete(10L, 130L); /** * 验证mock对象的行为 */ // 这两行一样,都是判断执行上面真实方法后,mock对象commentService是不是调用了1次deleteById(id)方法 // 处理times判断次数,还有很多方法可以判断mock对象方法调用的情况 // verify(commentService).deleteById(id); verify(commentService, times(1)).deleteById(id); /** * hutool对返回结果进行断言 */ Assert.isTrue(mobileResponse.getCode().equals("0")); Assert.isTrue(mobileResponse.getData().equals(true)); } }
情形二
@Service @Transactional @RequiredArgsConstructor(onConstructor = @__(@Autowired)) public class CommentServiceImpl extends ServiceImpl<CommentMapper, Comment> implements ICommentService { private final IHistoryService historyService; // 构造方法注入,单测时就可以注入mock对象,不然需要提供set方法注入mock对象 public CommentServiceImpl(IHistoryService historyService) { this.historyService = historyService; } public IHistoryService getHistoryService() { return historyService; } public void setHistoryService(IHistoryService historyService) { this.historyService = historyService; } @Override public MobileResponse commentDelete(Long id, Long userId) { //查询原留言 Comment originalComment = this.selectById(id); // ① 使用了this,调用本类方法,而不是通过传入本类的一个实例对象调用 //查询是否是自己的评论 if (originalComment.getCreateUserId().equals(userId.intValue())) { boolean result = this.deleteById(id); // ② 使用了this调用本类方法,而不是通过传入本类的一个实例对象调用 History history = new History(userId.intValue(), userId.intValue(), OperationTypeEnum.DELETE_COMMENT.getValue(), originalComment.getContent(), "-", originalComment.getInfoId()); result = historyService.insert(history); return MobileResponse.success(result); } return MobileResponse.error(MobileError.CAN_ONLY_DELETE_THEIR_OWN); } }
注意:①和②处,不写this也可以,测试方法一样,都是下面这种写法,此时也不需要注入本类的实例对象,只需要通过set方法设置需要用到的其他模拟对象,比如这里是historyService;
package com.stylefeng.guns.modular.detailRules.comment.service.impl; import cn.hutool.core.lang.Assert; import com.stylefeng.guns.core.base.protocol.MobileResponse; import com.stylefeng.guns.modular.detailRules.comment.model.Comment; import com.stylefeng.guns.modular.detailRules.history.model.History; import com.stylefeng.guns.modular.detailRules.history.model.enums.OperationTypeEnum; import com.stylefeng.guns.modular.detailRules.history.service.IHistoryService; import org.junit.Test; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import static org.mockito.Mockito.*; public class CommentServiceImplTest { /** * 该测试方法模拟用户有删除权限的场景; * * mock对象调用方法的参数类型和值内容必须完全一致,才能得到mock预计的返回值; * 比如这个:when(commentService.selectById(id)).thenReturn(originalComment); * 如果id为int型就不会触发该预计的返回,或者实际传递的id为10,该when中传递的id为9,内容不一致,也不会触发该预计的返回; * @param * @return void */ @Test public void commentDelete() { /** * 构造mock对象 */ Comment originalComment = mock(Comment.class); when(originalComment.getCreateUserId()).thenReturn(130); long id = 10; IHistoryService historyService = mock(IHistoryService.class); Long userId = 130L; History history = new History(userId.intValue(), userId.intValue(), OperationTypeEnum.DELETE_COMMENT.getValue(), originalComment.getContent(), "-", originalComment.getInfoId()); when(historyService.insert(history)).thenReturn(true); /** * 构造真实对象,调用方法 */ CommentServiceImpl mock = mock(CommentServiceImpl.class, CALLS_REAL_METHODS); mock.setHistoryService(historyService); // 设置测试类中调用方法selectById(id)时返回originalComment模拟对象,测试类中可以使用this.selectById(id),也可以直接使用selectById(id)调用 Mockito.doAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { return originalComment; } }).when(mock).selectById(id); // 设置测试类中调用方法deleteById(id)时返回true,测试类中可以使用this.deleteById(id),也可以直接使用deleteById(id)调用 Mockito.doAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { return true; } }).when(mock).deleteById(id); MobileResponse mobileResponse = mock.commentDelete(10L, 130L); // 这两行一样,都是判断执行上面真实方法后,mock对象commentService是不是调用了1次deleteById(id)方法 // verify(commentService).deleteById(id); // verify(commentService, times(1)).deleteById(id); /** * 对返回结果进行断言 */ Assert.isTrue(mobileResponse.getCode().equals("0")); Assert.isTrue(mobileResponse.getData().equals(true)); } }
带着疑问去思考,然后串联,进而归纳总结,不断追问自己,进行自我辩证,像侦查嫌疑案件一样看待技术问题,漆黑的街道,你我一起寻找线索,你就是技术界大侦探福尔摩斯