1.入口方法如何查找mock調用鏈
寫單測用例的時候,需要對入口方法涉及的各種實例進行mock,諸如數據庫操作,redis操作,rpc訪問等等,至於純內存計算的實例,只要是條件ok,則可以不必進行mock。
進行mock的時候,有些實例調用有可能隱藏的很深,假設我們沒有發現,有可能造成單測用例執行失敗,這就需要我們debug待測方法,列出需要進行mock的實例,然後一一操作即可。
單測用例生成框架, tiny-autounit,則是通過遞歸走查方法體,然後找到相關實例並進行mock,節省大量的查找時間。
2. 對ElasticSearch進行mock
系統中,如果用了es,且摻雜有比較複雜的邏輯,則需要對es進行mock,整體mock方式如下:
SearchResponse進行mock:
SearchResponse searchResponse = mock(SearchResponse. class ); SearchHits searchHits = mock(SearchHits. class ); when(searchResponse.getHits()).thenReturn(searchHits); SearchHit[] searchHits1 = new SearchHit[ 1 ]; searchHits1[ 0 ] = mock(SearchHit. class ); when(searchResponse.getHits().getHits()).thenReturn(searchHits1); when(searchHits1[ 0 ].getSourceAsString()).thenReturn( "{\n" + "\"updateDate\": \"2020-03-23 14:47:40\",\n" + "\"name\": \"業務建模說明\",\n" + "\"orderNum\": 10000,\n" + "\"updateUser\": \"wangxuanyi5\",\n" + "\"createUser\": \"taijian\",\n" + "\"showStatus\": 1,\n" + "\"documentContent\": {\n" + "\"updateDate\": \"2020-07-08 14:56:18\",\n" + "\"menuId\": 253,\n" + "\"updateUser\": \"chengtingwei\",\n" + "\"createUser\": \"taijian\",\n" + "\"documentStatus\": 1,\n" + "\"id\": 254,\n" + "\"content\": \"在充分了解前臺業務現狀得更快速高效。\",\n" + "\"createDate\": \"2020-03-22 16:34:13\"\n" + "},\n" + "\"id\": 253,\n" + "\"parentId\": 251,\n" + "\"createDate\": \"2020-03-22 16:34:13\"\n" + "}" ); TotalHits totalHits = new TotalHits( 10 , TotalHits.Relation.EQUAL_TO); when(searchResponse.getHits().getTotalHits()).thenReturn(totalHits); when(elasticsearchNativeOperation.search(Mockito.any(), Mockito.anyString())).thenReturn(searchResponse); |
UpdateResponse進行mock:
UpdateResponse updateResponse = mock(UpdateResponse. class ); |
BulkByScrollResponse進行mock:
BulkByScrollResponse bulkByScrollResponse = mock(BulkByScrollResponse. class ); |
3. 對static類進行mock
注意,@BeforeClass和@AfterClass要成對出現。
@BeforeClass public static void beforeClass(){ authorityUtilMockedStatic = Mockito.mockStatic(AuthorityUtil. class ); } @AfterClass public static void afterClass(){ authorityUtilMockedStatic.close(); } private static MockedStatic<AuthorityUtil> authorityUtilMockedStatic ; @Test public void when_list_then_return_success1(){ authorityUtilMockedStatic.when(()->AuthorityUtil.getUserErp()).thenReturn( "test" ); authorityUtilMockedStatic.when(()->AuthorityUtil.isPdAdmin()).thenReturn( false ); //todo biz List returnResult = pdProductInfoServiceImpl.list(); assert returnResult != null ; } |
4. 對入參類型進行mock過程中的注意事項
如果入參是自定義的類對象,則需要利用Mockito.any()來進行,也可以自己new出來一個新類來進行:
when(elasticsearchNativeOperation.search(Mockito.any())).thenReturn(searchResponse); |
如果入參既有自定義類對象,也有元數據類型,則可以用如下方式:
when(elasticsearchNativeOperation.search(Mockito.any(), Mockito.anyString())).thenReturn(searchResponse); 或者 when(elasticsearchNativeOperation.search(Mockito.any(), eq( "testSring" ))).thenReturn(searchResponse); |
千萬要注意的是,一旦參數中,有一個參數你用了Mockito.***,那麼其他參數你要麼用Mockito.***來替代, 要麼用eq(***具體的參數值***)來替代,不允許直接輸入參數值。
如果入參是Integer,但是你用了Mockito.any()來替代,大概率會出現nullpointer錯誤,這點需要注意,一定要用對替代類型。
5. 實例返回結果爲null
經常我們在打好mock樁之後,debug代碼中後,發現返回的結果爲null,怎麼設置參數都不行。實際上這種情況,是因爲你入參中有參數爲null造成的,此時,你需要將爲null的參數給處理爲非null的數據即可。
如果null參數數據比較難處理,你也可以在打樁的地方,直接給對應的參數設置爲 eq(null) 也可以,這樣實例返回結果就會返回你的打樁值了。
6. 單測方法一對多
一般一個業務方法是對用多個單測方法的,因爲有些分支條件,需要多個單測方法才能覆蓋完畢,所以不要吝嗇多寫單測方法,即便重複了,也沒事兒。
7. Exception異常類處理
異常類的話,一般在方法頭上打,不必自己進行捕獲,利用expected關鍵字即可。
@Test (expected = ComponentBusinessException. class ) public void when_addAppComponent_then_appname_null() { when(lockService.lock(eq(LockEnums.LockTypeEnums.REDIS), Mockito.anyString(), Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt())).thenReturn( false ); Component4AddAppEntity entity = new Component4AddAppEntity(); entity.setCurrentLimitLevel( "test" ); entity.setDeptName( "test" ); ComponentInfoEntity returnResult = componentServiceImpl.addAppComponent(entity); assert returnResult != null ; } |