单元测试用例编写避坑指南

 

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;
}

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章