单元测试问题讨论
代码功能
下单时校验优惠券
如果是新客券
校验H5中不能使用
校验每个设备只能一次
校验非新客不能使用新客券
其他校验
代码
public void checkCoupon(Foo foo){
if("new_customer_coupon".equals(foo.couponCode)) { //新客券
if ("h5".equals(foo.deviceType)) {
//来自H5不能使用新客券
throw new RuntimeException("h5不能使用新客券");
}
// 新客券每个设备只能一次
if(foo.deviceId != null){
int count = getCountByDeviceIdAndCouponCode(foo.deviceId, "new_customer_coupon");
if(count>0){
throw new RuntimeException("每个设备只能使用一次新客券");
}
}
// 非新客不能使用新客券
if(!foo.isNewCustomer){
throw new RuntimeException("只有新客才能使用新客券");
}
}
// 校验type为1的券
if(1 == foo.couponType){
//...
}
// 其他校验
otherCheck();
}
单元测试代码 反面验证H5中不能使用新客券
@Test
public void test_use_new_customer_coupon_in_h5(){
// 验证在H5中使用新客券
Foo foo = new Foo();
foo.couponCode = "new_customer_coupon";
foo.setDeviceType("h5");
try {
service.checkCoupon(foo);
fail();
} catch (RuntimeException e) {
Assert.assertEquals("h5不能使用新客券",e.getMessage());
}
}
不能只做反面的验证, 还得验证H5使用普通券(非新客券)的情况, 才能证明代码没有问题, 因为假如代码是下面这样的写法 上述验证也能通过
public void checkCoupon(Foo foo){
if("h5".equals(foo.deviceType)) { //来自h5
throw new RuntimeException("h5不能使用新客券");
}
//...
}
但显然这样的写法是有问题的。
于是验证H5使用普通券的情况 即使用普通券的时候没有进入校验新客券的逻辑中直接执行了下面的代码
@Test
public void test_use_normal_coupon_in_h5(){
// 验证在H5中使用非新客券
Foo foo = new Foo();
foo.couponCode = "normal_coupon";
foo.setDeviceType("h5");
try {
service.checkCoupon(foo);
fail();
} catch (NullPointerException e) {
}
}
因为couponType(Integer类型)未设置值 会导致空指针
// 校验type为1的券
if(1 == foo.couponType){...}
但这样的验证很别扭,也不保险。你不知道这个空指针异常就是这一代码导致的? 说不定还是校验新客券的逻辑中报的空指针异常呢?再说代码也会变化, 可能在校验type为1的券之前会做新的校验, 这时可能会报其他异常, 如RuntimeException
someNewCheck();
// 校验type为1的券
if(1 == foo.couponType){...}
所以如何优雅的验证H5使用普通券没有进入校验新客券的逻辑中且执行了后续的代码呢?