单元测试最佳实践示例汇总
示例一:变不确定为确定
业务
批量发放优惠券 如1张满100减50优惠券
, 2张满200减100优惠券
先去批量创建优惠券,再批量发放优惠券
for(CouponInfo info : couponInfoList){
Coupon coupon = new Coupon();
coupon.setCouponCode(UUIDGenerator.getUUID32()); // 动态随机生成优惠券编码 如84678bfd7c1011e6a22b4437e6d0648e
// ...
couponList.add(coupon);
}
couponService.batchAdd(couponList); //批量创建优惠券
// 得到Map<CouponCode, 发放张数>
couponService.batchSendCouponsToUser(userId, couponCodeCountMap); //批量发放优惠券给某一用户
问题
想校验优惠券实际发放情况 如满200减100
发了两张, 满100减50
发放了一张, 但是优惠券编码是随机生成的, 没办法在单元测试中得知券和编码的对应关系。
解决
将生成优惠券编码的信息提取到一个独立的方法中,protected修饰,子类可以覆盖。如下所示
protected String getUUID32(CouponInfo couponInfo) {
return UUIDGenerator.getUUID32();
}
然后单元测试中创建一个内部类继承欲测试的类并覆盖此方法
private static class MockBuyGiftsActivityService extends BuyGiftsActivityService{
@Override
protected String getUUID32(CouponInfo couponInfo) {
// couponCode同券满减信息 如100_50
return format("%d_%d",couponInfo.getReachPrice(),couponInfo.getCouponPrice());
}
}
}
于是单元测试可以这样测
@InjectMocks
private MockBuyGiftsActivityService service; // 实际测试的是该子类
// 验证批量发券
ArgumentCaptor<Map<String,Integer>> couponCodeCountMapCaptor = ArgumentCaptor.forClass(Map.class);
verify(couponService).batchSendCouponsToUser(eq(userId),couponCodeCountMapCaptor.capture());
Map<String, Integer> couponCodeCountMap = couponCodeCountMapCaptor.getValue();
assertEquals(2, couponCodeCountMap.size()); // 验证共发了两种券
assertEquals(1, couponCodeCountMap.get("100_50").intValue()); // 验证满100减50发了1张
assertEquals(2, couponCodeCountMap.get("200_100").intValue()); // 验证满200减100发了2张
示例二--修改Mock方法中的对象参数值
业务
同样的业务 只不过将注入couponCode
统一放到couponService
中去实现了
// 组装couponList
for(CouponInfo info : couponInfoList){
Coupon coupon = new Coupon();
// ...
couponList.add(coupon);
}
couponService.batchAdd(couponList); //批量创建优惠券
// 得到Map<CouponCode, 发放张数>
couponService.batchSendCouponsToUser(userId, couponCodeCountMap); //批量发放优惠券给某一用户
问题
在couponService.batchAdd
内注入couponCode值
for(Coupon coupon : couponList){
coupon.setCouponCode(UUIDGenerator.getUUID32()); // 动态随机生成优惠券编码 如84678bfd7c1011e6a22b4437e6d0648e
}
但单元测试实际不会进入mock方法内部
@Mock
private CouponService couponService;
导致单元测试中couponCode均为空 没办法进行后续验证 因为所有券的编码均为NULL
解决
可以采用下面的解决方法
doAnswer(new Answer() {
@Override
public Void answer(InvocationOnMock invocationOnMock) throws Throwable {
List<Coupon> couponList = invocationOnMock.getArgumentAt(0, List.class); //得到实际artisanCouponModelList参数
couponList.get(0).setCouponId("100_50"); // 显式指定满100减50元的编码
couponList.get(1).setCouponId("200_100"); // 显式指定满200减100的编码
return null;
}
}).when(couponService).batchAdd(anyList());
相当于在此修改了mock方法中的对象参数 于是后面验证券的发放情况 就方便了 如下所示
// 验证批量发券
ArgumentCaptor<Map<String,Integer>> couponCodeCountMapCaptor = ArgumentCaptor.forClass(Map.class);
verify(couponService).batchSendCouponsToUser(eq(userId),couponCodeCountMapCaptor.capture());
Map<String, Integer> couponCodeCountMap = couponCodeCountMapCaptor.getValue();
assertEquals(2, couponCodeCountMap.size()); // 验证共发了两种券
assertEquals(1, couponCodeCountMap.get("100_50").intValue()); // 验证满100减50发了1张
assertEquals(2, couponCodeCountMap.get("200_100").intValue()); // 验证满200减100发了2张
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。