zoukankan      html  css  js  c++  java
  • 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));
        }
    }

    测试结果(秒级完成)

    情形二

    如果被单测的方法中使用了this调用本类方法,如下:

    @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));
        }
    }
    带着疑问去思考,然后串联,进而归纳总结,不断追问自己,进行自我辩证,像侦查嫌疑案件一样看待技术问题,漆黑的街道,你我一起寻找线索,你就是技术界大侦探福尔摩斯
  • 相关阅读:
    win10+Linux双系统安装及一些配置问题
    第3讲--3.1旋转矩阵
    【读诗】宣州谢朓楼饯别校书叔云
    【2】python:end=' '
    如何与国外导师联系
    PointNet
    点云深度学习
    ES6常用方法
    监听滚动条、上下联动
    echarts 左右滚动
  • 原文地址:https://www.cnblogs.com/cainiao-Shun666/p/14806725.html
Copyright © 2011-2022 走看看