使用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模塊的啟動類的啟動注解給注釋掉,否則測試啟動會報錯


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM