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要成对出现。
@BeforeClasspublic static void beforeClass(){
authorityUtilMockedStatic = Mockito.mockStatic(AuthorityUtil.class);
}@AfterClasspublic static void afterClass(){
authorityUtilMockedStatic.close();
}private static MockedStatic<AuthorityUtil> authorityUtilMockedStatic ;
@Testpublic 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;
} |