观察身边人的不同工作阶段的时间占比可以看得出来,其实编写代码的时间只占很少的一部分,而调试代码花费的时间是最多的。我想各位战友都有过为了修复一个bug而花费一整天甚至更长时间的经历。对于bug而言,可能并不是很难修复,而困难的点往往在于找出问题的原因,而且稍不注意往往就会造成另一个bug的悄悄产生。所以如何保证修改后代码的正确性,作者认为测试代码往往起着决定性的作用。对代码的修改往往是在不改变代码行为的基础上改变代码的结构,而单元测试正是确保这种行为不被改变的至关重要的一环。没有完善的单元测试,修改的每一步都可能引入难以察觉的缺陷。重构中虽然主要关注点是已有代码的改进,但是也有体现测试驱动设计的思想。好的单元测试不仅验证功能正确性,更促进了良好设计的产生。当代码易于测试时,通常也意味着它具有良好的设计质量。良好的测试代码具有以下特点
1 、快速执行
2、独立性
每个测试用例应该是独立执行互不影响的。这就要求测试执行的结果,不依赖于其他测试的执行顺序、执行结果或一些共享的变量。我认为独立性最重要的作用,应该是为测试代码的并行执行提供基础题条件,如果测试代码执行相互存在依赖,无论是直接引用也好、结果的依赖也好,都会造成测试代码无法并行执行。
3、可重复性
测试代码在任意环境中执行后的结果应该是一样的,不应该受到时间、随机性数、网络状态等的影响。
我认为这就要求我们在编写测试代码时,做好独立的数据准备、外部依赖的模拟准备、上下文的模拟等,并且关键数据的准备不能存在随机性,以免破坏测试代码的可重复性。在测试完成后要对这部分准备的数据进行清理,以保证可以重复执行。
public bool Prepare()
{
CurrentUser user = new CurrentUser()
{
CustomerID = "testpluscustomer"
};
SessionProvider.SetSessionIfNotExist(user);
InnerSGHelper.IfTest = true;
List cards = new List()
{
new CardTicket{ CardTicketType ="3",CouponType="1",CouponDisCount=5,CouponMaxDeductMoney=5,CouponLimitMoney=1,DeductFeeType="1",Money=1}
};
InnerSGHelper.Result["VSM-Inner-GetCardTicketInfoByOrder"] = cards;
string type = "insert";
string sql = Plus_baseplanSQL(type);
_boss.Insert(sql);
return true;
}
public bool After()
{
string type = "delete";
string sql = Plus_baseplanSQL(type);
_boss.Delete(sql);
RedisHelper.GetInstance().RemoveAll(new List()
{
"PLUS-AvaBasePlanList",
"PLUS-CouponList",
"PLUS-BaseInfo",
"PLUS-NotBaseInfo"
});
return true;
}4、自验证
测试代码应该能够自己判断是否通过,不能再由人工去比对测试结果。数据比对可以通过合理的断言来实现。
Assert.IsTrue(res != null);
Assert.IsTrue(res.productVersion == "trueResult");
Assert.IsTrue(res.count == 3);5、及时性
测试代码应该与业务代码同步,当修改业务代码时应该同步修改测试代码,并且及时执行测试来知道修改的正确性。我认为在每次修改完代码后立即跑一下单元测试,这样写出来的代码是最不容易出错的,前提是有比较可靠的单元测试基础。这就要求在功能累积的同时,也不断累积这样一些可靠的测试代码,为后续的修改打下良好的基础。
6、结构合理
测试代码的结构应分为准备、执行、验证三部分。
public void ProfileTest()
{
ParamV3Model param = new ParamV3Model();
if (Prepare())
{
try
{
var res = _service.Profile(param);
Assert.IsTrue(res != null);
Assert.IsTrue(res.productVersion == "trueResult");
}
catch (Exception ex)
{
Assert.IsTrue(false);
}
finally
{
After();
}
}
else
{
Assert.IsTrue(false);
}
}