如何使用Mockito調(diào)用靜態(tài)方法和void方法
一、mock 靜態(tài)方法
mockito庫(kù)并不能mock靜態(tài)方法,需要依賴powermock
第一步:給類添加注解
// 靜態(tài)類優(yōu)先加載,所以需要提前告訴powermock哪些靜態(tài)類需要mock
@ContextConfiguration
@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)
@PrepareForTest(靜態(tài)調(diào)用類.class)
public class SupplierServiceImplTest extends PowerMockTestCase {}
第二步:mock使用
@Test(expectedExceptions = BusinessException.class)
public void testAddSupplierAccount_genIdentityNoError() {
// 告訴powermock,需要mock該類的所有靜態(tài)方法
PowerMockito.mockStatic(PasswordGenerator.class);
final SupplierAccountDto supplierAccountDto = new SupplierAccountDto();
supplierAccountDto.setName("小明");
final String randomPWd = "666";
PowerMockito.when(supplierDao.selectByEmail(anyString()))
.thenReturn(new ArrayList<HaitaoSupplier>());
// 靜態(tài)方法mock
PowerMockito.when(PasswordGenerator.genPwd()).thenReturn(randomPWd);
PowerMockito.when(pwEncoder.encode(anyString())).thenReturn(randomPWd);
PowerMockito.when(identityNoGenerator.genIdentityNo()).thenReturn(-1L);
supplierServiceImpl.addSupplierAccount(supplierAccountDto);
verify(pwEncoder).encode(randomPWd);
}
二、mock void 方法
// void嘛,doNothing顧名思義 PowerMockito.doNothing().when(casService).addSupplier(anyLong(), any(ServiceKey.class));
使用PowerMockito和Mockito進(jìn)行模擬測(cè)試
包括靜態(tài)方法測(cè)試,私有方法測(cè)試等,以及方法執(zhí)行的坑或者模擬不成功解決
一 普通spring項(xiàng)目
依賴:這個(gè)很重要,不同版本用法也有點(diǎn)區(qū)別:
<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>
接下來(lái)就是mock測(cè)試了,使用完全模擬測(cè)試過(guò)程,對(duì)于需要測(cè)試接口中調(diào)用的靜態(tài),私有方法等,返回自己想要的預(yù)期結(jié)果,達(dá)到測(cè)試效果:
這里有幾個(gè)要點(diǎn):
測(cè)試過(guò)程中完全手動(dòng)mock,不會(huì)真實(shí)調(diào)用或者產(chǎn)生數(shù)據(jù)
一 mock對(duì)象
order = mock(Order.class); user = mock(User.class);
二 屬性注入
將service等類中需要的其他service或者mapper等mock出來(lái),然后分別使用工具類注入,名稱保持一致即可
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);
三 靜態(tài)方法mock
模擬靜態(tài)方法返回結(jié)果需要使用PowerMockit,測(cè)試類上必須加注解@PrepareForTest
//account 獲取stub PowerMockito.mockStatic(Account.class); Mockito.when(Account.get(anyString(), anyString(), anyString(), anyInt())).thenReturn(account);
四 私有方法
私有方法首先需要在類上加入注解,對(duì)于要測(cè)試的類中的public方法同樣有效,比如測(cè)試方法中包含一個(gè)public方法,可以同樣模擬:
@PrepareForTest(ConsumptionServiceImpl.class) //里面寫需要模擬私有方法的類class
然后對(duì)象不能mock,必須new一個(gè),并且需要用spy處理:
orderService = PowerMockito.spy(new OrderServiceImpl());
接著使用doreturn .when這種形式模式,不能使用先when后return這種,會(huì)報(bào)錯(cuò)
注意一點(diǎn),模擬參數(shù)要么全部模擬,要么全部自定義,不能混搭
這里有個(gè)大坑,如果出現(xiàn)私有方法還是進(jìn)去執(zhí)行的情況,很大可能是參數(shù)不對(duì),比如你mock的參數(shù)是 anyString(),那么你真是測(cè)試時(shí)候傳遞的必須是一個(gè)String實(shí)例,不能是null,否則mock就會(huì)失敗,我這里之前一直是對(duì)象的一個(gè)屬性,直接new了一個(gè)對(duì)象傳遞
所以一直不成功:
比如 方法需要的是user.getId() ,而且你mock的是一個(gè)anyInt(),那么真正傳遞的時(shí)候必須給這個(gè)user,setId(9527),否則就無(wú)法達(dá)成預(yù)期的模擬效果,所有方法都一樣!!
try {<br> // 方法名,方法參數(shù),必須全部對(duì)應(yīng),否則報(bào)錯(cuò)方法找不到
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();
}
五 預(yù)期結(jié)果
verify :判斷方法執(zhí)行了幾次: 確定測(cè)試是否通過(guò)
例如:verify(userService, times(1)).queryUser(any(anyInt(),anyString(),anyString());
二 springboot項(xiàng)目使用
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 創(chuàng)建測(cè)試基類
/**
* 測(cè)試基類,所有子測(cè)試類繼承此類即可
*/
@PowerMockRunnerDelegate(SpringRunner.class)
@RunWith(PowerMockRunner.class)
@PowerMockIgnore({"javax.management.*", "javax.security.*"}) //忽略一些mock異常
@SpringBootTest
public class TestBase {
}
3 創(chuàng)建特定的測(cè)試類
public class HotelControllerTest extends TestBase { //繼承基類即可
@Mock
private HotelService hotelService;
private Integer id;
// 加載springContext進(jìn)行mock測(cè)試,真實(shí)調(diào)用方法,不需要mock步驟
// @Autowired
// private HotelController hotelController;
// 純mock測(cè)試,不加載springContext,執(zhí)行mock操作,必須mock步驟,不會(huì)真實(shí)調(diào)用
@InjectMocks
private HotelController hotelController=new HotelController();
// 應(yīng)用到所有門店測(cè)試
@Test
public void detailTest(){
System.out.println("test start.....");
// 1 構(gòu)造參數(shù)
ininParams(1);
// 2 mock步驟
mockStep();
// 3 執(zhí)行操作
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 模擬私有方法和靜態(tài)方法
@PrepareForTest(OrderServiceImpl.class) // 需要調(diào)用私有或者靜態(tài)方法的類
public class OrderControllerTest extends TestBase {
private OrderServiceImpl orderServiceImpl; //需要調(diào)用私有或者靜態(tài)方法時(shí),不能使用@Mock,還需要@before初始化屬性
@Mock
private OrderMapper orderMapper;
@Mock
private RestTemplateUtil restTemplateUtil;
private Integer orderId;
private String wxcid;
@Before
public void init(){
//處理私有方法模擬實(shí)例
orderServiceImpl = PowerMockito.spy(new OrderServiceImpl()); //使用spy模擬的需要手動(dòng)注入屬性,因?yàn)槭裁炊紱]有
ReflectionTestUtils.setField(orderController, "iOrderService", orderServiceImpl);
ReflectionTestUtils.setField(orderServiceImpl, "orderMapper", orderMapper);
ReflectionTestUtils.setField(orderServiceImpl, "restTemplateUtil", restTemplateUtil);
}
//純mock測(cè)試,不加載springContext,執(zhí)行mock操作,必須mock步驟,不會(huì)真實(shí)調(diào)用
@InjectMocks
private OrderController orderController=new OrderController();
@Test
public void cancelTest(){
System.out.println("test start.....");
// 1 構(gòu)造參數(shù)
ininParams();
// 2 mock步驟
mockStep();
// 3 執(zhí)行操作
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;
}
}
注意:
如果是分模塊項(xiàng)目,springboot項(xiàng)目的啟動(dòng)類只能有一個(gè),即需要把其他service,dao,common模塊的啟動(dòng)類的啟動(dòng)注解給注釋掉,否則測(cè)試啟動(dòng)會(huì)報(bào)錯(cuò)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
使用Java開發(fā)實(shí)現(xiàn)OAuth安全認(rèn)證的應(yīng)用
這篇文章主要介紹了使用Java開發(fā)實(shí)現(xiàn)OAuth安全認(rèn)證的應(yīng)用的方法,OAuth安全認(rèn)證經(jīng)常出現(xiàn)于社交網(wǎng)絡(luò)API應(yīng)用的相關(guān)開發(fā)中,需要的朋友可以參考下2015-11-11
簡(jiǎn)單總結(jié)Java IO中stream流的使用方法
這篇文章主要介紹了Java IO中stream流的使用方法的簡(jiǎn)單總結(jié),包括數(shù)據(jù)流和打印流等Java入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2016-03-03
Spring中的后置處理器BeanPostProcessor詳解
這篇文章主要介紹了Spring中的后置處理器BeanPostProcessor詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-08-08
詳解spring boot實(shí)現(xiàn)websocket
這篇文章主要介紹了詳解spring boot實(shí)現(xiàn)websocket,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-06-06
java LocalDateTime加時(shí)間,計(jì)算兩個(gè)時(shí)間的差方式
文章介紹了如何在Java中使用LocalDateTime類添加時(shí)間并計(jì)算兩個(gè)時(shí)間的差值,通過(guò)比較來(lái)總結(jié)個(gè)人經(jīng)驗(yàn),并鼓勵(lì)讀者參考和支持腳本之家2025-03-03

