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( "{
" + ""updateDate": "2020-03-23 14:47:40",
" + ""name": "业务建模说明",
" + ""orderNum": 10000,
" + ""updateUser": "wangxuanyi5",
" + ""createUser": "taijian",
" + ""showStatus": 1,
" + ""documentContent": {
" + ""updateDate": "2020-07-08 14:56:18",
" + ""menuId": 253,
" + ""updateUser": "chengtingwei",
" + ""createUser": "taijian",
" + ""documentStatus": 1,
" + ""id": 254,
" + ""content": "在充分了解前台业务现状得更快速高效。",
" + ""createDate": "2020-03-22 16:34:13"
" + "},
" + ""id": 253,
" + ""parentId": 251,
" + ""createDate": "2020-03-22 16:34:13"
" + "}" ); 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 ; } |