• 使用PowerMockito和Mockito进行模拟测试,包括静态方法测试,私有方法测试等,以及方法执行的坑或者模拟不成功解决


    一 普通spring项目

    依赖:这个很重要,不同版本用法也有点区别:

    		<dependency>
    			<groupId>org.mockito</groupId>
    			<artifactId>mockito-all</artifactId>
    			<version>2.0.2-beta</version>
    			<scope>test</scope>
    		</dependency>
    
    		<dependency>
    			<groupId>org.powermock</groupId>
    			<artifactId>powermock-api-mockito</artifactId>
    			<version>1.7.4</version>
    			<scope>test</scope>
    		</dependency>
    		<dependency>
    			<groupId>org.powermock</groupId>
    			<artifactId>powermock-module-junit4</artifactId>
    			<version>2.0.0</version>
    			<scope>test</scope>
    		</dependency>
    
    		<dependency>
    			<groupId>org.powermock</groupId>
    			<artifactId>powermock-core</artifactId>
    			<version>1.7.4</version>
    			<scope>test</scope>
    		</dependency>
    

      接下来就是mock测试了,使用完全模拟测试过程,对于需要测试接口中调用的静态,私有方法等,返回自己想要的预期结果,达到测试效果:

    这里有几个要点:

    测试过程中完全手动mock,不会真实调用或者产生数据

    一  mock对象

    order = mock(Order.class);
    user = mock(User.class);

    二 属性注入

    将service等类中需要的其他service或者mapper等mock出来,然后分别使用工具类注入,名称保持一致即可

            roomTypeService = mock(RoomTypeServiceImpl.class);
            ticketComponetService = mock(TicketComponetServiceImpl.class);
            hotelMapper = mock(HotelMapper.class);
            //注入属性
            ReflectionTestUtils.setField(orderService, "hotelGroupMapper", hotelGroupMapper);
            ReflectionTestUtils.setField(orderService, "dsUtils", dsUtils);
            ReflectionTestUtils.setField(orderService, "orderMapper", orderMapper);

    三 静态方法mock

    模拟静态方法返回结果需要使用PowerMockit,测试类上必须加注解@PrepareForTest

            //account 获取stub
            PowerMockito.mockStatic(Account.class);
            Mockito.when(Account.get(anyString(), anyString(), anyString(), anyInt())).thenReturn(account);

    四 私有方法

    私有方法首先需要在类上加入注解,对于要测试的类中的public方法同样有效,比如测试方法中包含一个public方法,可以同样模拟:
        @PrepareForTest(ConsumptionServiceImpl.class)  //里面写需要模拟私有方法的类class
        然后对象不能mock,必须new一个,并且需要用spy处理:
        orderService = PowerMockito.spy(new OrderServiceImpl());
    接着使用doreturn  .when这种形式模式,不能使用先when后return这种,会报错
    注意一点,模拟参数要么全部模拟,要么全部自定义,不能混搭
    这里有个大坑,如果出现私有方法还是进去执行的情况,很大可能是参数不对,比如你mock的参数是 anyString(),那么你真是测试时候传递的必须是一个String实例,不能是null,否则mock就会失败,我这里之前一直是对象的一个属性,直接new了一个对象传递
    所以一直不成功:
    比如 方法需要的是user.getId() ,而且你mock的是一个anyInt(),那么真正传递的时候必须给这个user,setId(9527),否则就无法达成预期的模拟效果,所有方法都一样!!
           try {
            // 方法名,方法参数,必须全部对应,否则报错方法找不到 PowerMockito.doReturn(1).when(orderService, "dateListMinBook",anyString(),anyString(),any(RoomType.class),anyString(),anyString()); PowerMockito.doReturn(ResponseMessage.success().pushData("dateRoomTypeList",new ArrayList<DateRoomType>())).when(orderService, "eachDateNumAndPrice",any(Order.class),any(RoomType.class),anyBoolean(),anyInt(),anyString(),any(User.class)); PowerMockito.doReturn("2000").when(orderService, "getKeeptimeByWxcidAndHotelidAndLevel",anyString(),anyString(),anyString()); PowerMockito.doNothing().when(orderService, "getPayWay",any(),any(),any(),any(),any()); } catch (Exception e) { e.printStackTrace(); }

    五 预期结果

    verify :判断方法执行了几次: 确定测试是否通过

    例如:verify(userService, times(1)).queryUser(any(anyInt(),anyString(),anyString()); 

    二 springboot项目使用

    1 依赖

            <!-- S-junit -->
            <dependency>
                <groupId>org.mockito</groupId>
                <artifactId>mockito-all</artifactId>
                <version>2.0.2-beta</version>
                <scope>test</scope>
            </dependency>
    
            <dependency>
                <groupId>org.powermock</groupId>
                <artifactId>powermock-api-mockito2</artifactId>
                <version>2.0.0-beta.5</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.powermock</groupId>
                <artifactId>powermock-module-junit4</artifactId>
                <version>2.0.0-beta.5</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.powermock</groupId>
                <artifactId>powermock-core</artifactId>
                <version>2.0.0-RC.4</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>cglib</groupId>
                <artifactId>cglib</artifactId>
                <version>3.2.9</version>
            </dependency>
            <dependency>
                <groupId>org.mockito</groupId>
                <artifactId>mockito-inline</artifactId>
                <version>2.15.0</version>
            </dependency>
            <dependency>
                <groupId>org.assertj</groupId>
                <artifactId>assertj-core</artifactId>
                <version>3.12.2</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.easymock</groupId>
                <artifactId>easymock</artifactId>
                <version>4.0.2</version>
                <scope>test</scope>
            </dependency>
            <!-- E-junit -->

    2 创建测试基类

    /**
     * 测试基类,所有子测试类继承此类即可
     */
    @PowerMockRunnerDelegate(SpringRunner.class)
    @RunWith(PowerMockRunner.class)
    @PowerMockIgnore({"javax.management.*", "javax.security.*"}) //忽略一些mock异常
    @SpringBootTest
    public class TestBase {
    }

    3 创建特定的测试类

    public class HotelControllerTest extends TestBase { //继承基类即可
    
        @Mock
        private HotelService hotelService;
    
        private Integer id;
    
    //  加载springContext进行mock测试,真实调用方法,不需要mock步骤
    //     @Autowired
    //     private HotelController hotelController;
    
    //    纯mock测试,不加载springContext,执行mock操作,必须mock步骤,不会真实调用
        @InjectMocks
        private HotelController hotelController=new HotelController();
    
        // 应用到所有门店测试
        @Test
        public void detailTest(){
            System.out.println("test start.....");
            // 1 构造参数
            ininParams(1);
            // 2 mock步骤
            mockStep();
            // 3 执行操作
            ResponseMessage result = hotelController.detail(id);
            System.out.println(new Gson().toJson(result));
            assertEquals(0, (int) result.getCode());
        }
    
        private void mockStep() {
            when(hotelService.detail(anyInt())).thenReturn(ResponseMessage.success());
        }
    
        private void ininParams(Integer type) {
            switch(type){
                case 1:
                    id=17317;
                    break;
                case 2:
                    id=2;
                    break;
                default:
                    break;
            }
        }
    }

    4 模拟私有方法和静态方法

    @PrepareForTest(OrderServiceImpl.class)  // 需要调用私有或者静态方法的类
    public class OrderControllerTest extends TestBase {
    
        private OrderServiceImpl orderServiceImpl; //需要调用私有或者静态方法时,不能使用@Mock,还需要@before初始化属性
    
        @Mock
        private OrderMapper orderMapper;
        @Mock
        private RestTemplateUtil restTemplateUtil;
        private Integer orderId;
        private String wxcid;
    
        @Before
        public void init(){
            //处理私有方法模拟实例
            orderServiceImpl = PowerMockito.spy(new OrderServiceImpl()); //使用spy模拟的需要手动注入属性,因为什么都没有
            ReflectionTestUtils.setField(orderController, "iOrderService", orderServiceImpl);
            ReflectionTestUtils.setField(orderServiceImpl, "orderMapper", orderMapper);
            ReflectionTestUtils.setField(orderServiceImpl, "restTemplateUtil", restTemplateUtil);
        }
    
        //纯mock测试,不加载springContext,执行mock操作,必须mock步骤,不会真实调用
        @InjectMocks
        private OrderController orderController=new OrderController();
    
        @Test
        public void cancelTest(){
            System.out.println("test start.....");
            // 1 构造参数
            ininParams();
            // 2 mock步骤
            mockStep();
            // 3 执行操作
            ResponseMessage cancel = orderController.cancel(wxcid, orderId);
            assertEquals(0,(int)cancel.getCode());
        }
    
        private void mockStep() {
            Order order = new Order();
            order.setStatus(2);
            when(orderMapper.getOrderByOrderId(anyInt())).thenReturn(order);
            when(orderMapper.updateStatus(anyInt(),anyInt())).thenReturn(2);
            JsonObject jsonObject = new JsonObject();
            jsonObject.addProperty("code",0);
            when(restTemplateUtil.postToCri(anyString(),anyString(),any())).thenReturn(jsonObject);
            //处理私有方法,必须用这种写法
            try {
                PowerMockito.doNothing().when(orderServiceImpl, "returnTicketTouser", anyString(),any());
                PowerMockito.doReturn(ErrorCode.SUCCESS).when(orderServiceImpl, "refoundAndGetCode", any(),any(),any(),any());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        private void ininParams() {
            wxcid="57af462dff475fe4644de32f08406aa8";
            orderId=25864;
        }
    }

    注意:

    如果是分模块项目,springboot项目的启动类只能有一个,即需要把其他service,dao,common模块的启动类的启动注解给注释掉,否则测试启动会报错
  • 相关阅读:
    Redis list数据结构
    Redis hash结构
    大公司都有哪些开源项目~~~阿里,百度,腾讯,360,新浪,网易,小米等
    使用Nexus搭建Maven私服
    Maven nexus 安装nexus私服出现的两个问题
    SONATYPE NEXUS 搭建MAVEN 私服
    http://www.cnblogs.com/jqyp/archive/2010/08/20/1805041.html
    将 Smart 构件发布到 Maven 中央仓库
    http://www.ruanyifeng.com/blog/2013/07/gpg.html
    http://www.jianshu.com/p/1e402922ee32/
  • 原文地址:https://www.cnblogs.com/houzheng/p/10716684.html
Copyright © 2020-2023  润新知