如何使用Mockito調(diào)用靜態(tài)方法和void方法
一、mock 靜態(tài)方法
mockito庫(kù)并不能mock靜態(tài)方法,需要依賴(lài)powermock
第一步:給類(lèi)添加注解
// 靜態(tài)類(lèi)優(yōu)先加載,所以需要提前告訴powermock哪些靜態(tài)類(lèi)需要mock @ContextConfiguration @RunWith(PowerMockRunner.class) @PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class) @PrepareForTest(靜態(tài)調(diào)用類(lèi).class) public class SupplierServiceImplTest extends PowerMockTestCase {}
第二步:mock使用
@Test(expectedExceptions = BusinessException.class) public void testAddSupplierAccount_genIdentityNoError() { // 告訴powermock,需要mock該類(lèi)的所有靜態(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)目
依賴(lài):這個(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等類(lèi)中需要的其他service或者mapper等mock出來(lái),然后分別使用工具類(lèi)注入,名稱(chēng)保持一致即可
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è)試類(lèi)上必須加注解@PrepareForTest
//account 獲取stub PowerMockito.mockStatic(Account.class); Mockito.when(Account.get(anyString(), anyString(), anyString(), anyInt())).thenReturn(account);
四 私有方法
私有方法首先需要在類(lèi)上加入注解,對(duì)于要測(cè)試的類(lèi)中的public方法同樣有效,比如測(cè)試方法中包含一個(gè)public方法,可以同樣模擬:
@PrepareForTest(ConsumptionServiceImpl.class) //里面寫(xiě)需要模擬私有方法的類(lèi)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 依賴(lài)
<!-- 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è)試基類(lèi)
/** * 測(cè)試基類(lèi),所有子測(cè)試類(lèi)繼承此類(lèi)即可 */ @PowerMockRunnerDelegate(SpringRunner.class) @RunWith(PowerMockRunner.class) @PowerMockIgnore({"javax.management.*", "javax.security.*"}) //忽略一些mock異常 @SpringBootTest public class TestBase { }
3 創(chuàng)建特定的測(cè)試類(lèi)
public class HotelControllerTest extends TestBase { //繼承基類(lèi)即可 @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)用到所有門(mén)店測(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)方法的類(lè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)槭裁炊紱](méi)有 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); //處理私有方法,必須用這種寫(xiě)法 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)類(lèi)只能有一個(gè),即需要把其他service,dao,common模塊的啟動(dòng)類(lèi)的啟動(dòng)注解給注釋掉,否則測(cè)試啟動(dòng)會(huì)報(bào)錯(cuò)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot中@ComponentScan的使用詳解
這篇文章主要介紹了SpringBoot中@ComponentScan的使用詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11MyBatis的JdbcType與Oracle、MySql數(shù)據(jù)類(lèi)型一覽表
這篇文章主要介紹了MyBatis的JdbcType與Oracle、MySql數(shù)據(jù)類(lèi)型一覽表,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01java Hibernate多對(duì)多映射詳解及實(shí)例代碼
這篇文章主要介紹了java Hibernate多對(duì)多映射詳解及實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2017-01-01Java中的Collections類(lèi)的使用示例詳解
Collections類(lèi)提供了一些靜態(tài)方法,這些方法能夠?qū)ist集合實(shí)現(xiàn)常用的算法操作,這些算法是排序,填充,移位和查找等。本文將通過(guò)示例為大家詳細(xì)講講Collections類(lèi)的使用,需要的可以參考一下2022-12-12Java排序算法三之歸并排序的遞歸與非遞歸的實(shí)現(xiàn)示例解析
這篇文章主要介紹了Java排序算法三之歸并排序的遞歸與非遞歸的實(shí)現(xiàn)示例解析,文章通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08SpringBoot整合Hashids實(shí)現(xiàn)數(shù)據(jù)ID加密隱藏的全過(guò)程
這篇文章主要為大家詳細(xì)介紹了SpringBoot整合Hashids實(shí)現(xiàn)數(shù)據(jù)ID加密隱藏的全過(guò)程,文中的示例代碼講解詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-01-01Bean?Searcher配合SpringBoot的使用詳解
這篇文章主要介紹了Bean?Searcher配合SpringBoot的使用,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-06-06xxl-job對(duì)比ElasticJob使用示例詳解
這篇文章主要為大家介紹了xxl-job對(duì)比ElasticJob使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06使用Spring從YAML文件讀取內(nèi)容映射為Map方式
這篇文章主要介紹了使用Spring從YAML文件讀取內(nèi)容映射為Map方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02