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