map.put("total", (null == total) ? 0 : total);
return map;
}
要编写这个函数的单测,你需要 mock creativeService。对,mock 的目的实际上只是为了拿到模拟的 creativeDTOs 和 total 值,然后塞入 map。 最后验证 map 里是否有 rows 和 total 两个 key 以及值是否正确。
我讨厌 mock !引入一堆繁重的东西,mock 的代码并不比实际的产品代码少,而且很无聊 ! 对于懒惰的人来说,写更多跟产品和测试“没关系”的代码就是惩罚!有没有办法呢? 实际上,可以采用函数接口来隔离这些外部依赖服务。 见如下改写后的代码: getListFunc 表达了如何根据 CreativeQuery 得到 CreativeDO 的列表, getTotalFunc 表达了如何根据 CreativeQuery 得到 CreativeDO 的总数。 原来的 searchForSelect 方法只要传入两个 lambda 表达式即可。
public Map<String, Object> searchForSelect(@RequestParam(value = "k", required = false) String title,
@RequestParam(value = "page", defaultValue = "1") Integer page,
@RequestParam(value = "rows", defaultValue = "10") Integer pageSize) {
CreativeQuery query = buildCreativeQuery(title, page, pageSize);
return searchForSelect2(query,
(q) -> creativeService.search(q),
(q) -> creativeService.count(q));
}
public Map<String, Object> searchForSelect2(CreativeQuery query,
Function<CreativeQuery, List<CreativeDO>> getListFunc,
Function<CreativeQuery, Integer> getTotalFunc) {
List<CreativeDO> creativeDTOs = getListFunc.apply(query);
Integer total = getTotalFunc.apply(query);
Map<String, Object> map = new HashMap<String, Object>();
map.put("rows", (null == creativeDTOs) ? new ArrayList<CreativeDO>() : creativeDTOs);
map.put("total", (null == total) ? 0 : total);
return map;
}
/*
* NOTE: can be placed in class QueryBuilder
*/
public CreativeQuery buildCreativeQuery(String title, Integer page, Integer pageSize) {
CreativeQuery query = new CreativeQuery();
query.setTitle(title);
query.setPageNum(page);
query.setPageSize(pageSize);
return query;
}
现在,如何编写单测呢? buildCreativeQuery 这个自不必说。 实际上,只需要对 searchForSelect2 做单测,因为这个承载了主要内容; 而 searchForSelect 只是流程的东西,通过联调就可以测试。单测代码如下:
public class CreativeControllerTest {
CreativeController controller = new CreativeController();
@Test
public void testSearchForSelect2() {
CreativeQuery creativeQuery = controller.buildCreativeQuery("haha", 1, 20);
Map<String, Object> result = controller.searchForSelect2(creativeQuery,
(q) -> null , (q)-> 0);
Assert.assertEquals(0, ((List)result.get("rows")).size());
Assert.assertEquals(0, ((Integer)result.get("total")).intValue());
}
}
注意到,这里使用了 lambda 表达式来模拟返回外部服务的返回结果,因为我们本身就用 Function 接口隔离和模拟了外部服务依赖。 细心的读者一定发现了: lambda 表达式,简直是单测的 Mock 神器啊!
It’s Time to Say Goodbye to Mock Test Framework !
改写业务代码
看一段常见的业务代码,通过外部服务获取订单的物流详情后,做一段处理,然后返回相应的结果。
private List<Integer> getOrderSentIds(long sId, String orderNo) {
OrderParam param = ParamBuilder.buildOrderParam(sId, orderNo);
PlainResult<List<OrderXXXDetail>> xxxDetailResult =
orderXXXService.getOrderXXXDetailByOrderNo(param);
if (!xxxDetailResult.isSuccess()) {
return Lists.newArrayList();
}
List<OrderXXXDetail> xxxDetails = xxxDetailResult.getData();
List<Integer> sentIds = Lists.newArrayList();
xxxDetails.forEach(xxxDetail -> sentIds.add(xxxDetail.getId()));
return sentIds;
}
从第三行 if 到 return 的是一个不依赖于外部服务的独立函数。为了便于写单测,实际上应该将这一部