}
[TestMethod()]
[ExpectedException(typeof(ArgumentNullException))]
public void ConstructorTestBlank()
{
User target = new User(" ");
}
如果不修改类库中的代码,单元测试会报告这三个新的测试都失败了。
小飞对代码做了相应的修改。结果出了这样的错误,见代码清单11-5:
代码清单11-5
Test method UserTest.UserTest.ConstructorTestBlank threw exception System.ArgumentException, but exception System. ArgumentNull- Exception was expected. Exception message: System.Argument- Exception: Value does not fall within the expected range.
大家定睛一看,原来小飞的Copy/Paste用了原来的ArgumentNullExcep- tion,而不是ArgumentException。
如果有人加了下面的代码:
if (!m_email.Contains("@"))
{
throw new ArgumentException();
}
这时,代码覆盖测试就会报告代码覆盖率是85%左右。那还得加上新的单元测试以保证所有的代码都得到了基本的测试。
二柱:现在我知道为什么有些软件写了好几年都没有发布了,敢情他们都忙着写单元测试了。
阿超:也许因为他们没有在一开始就写单元测试,所以后来有很多小强要处理。很多调查显示,在软件开发后期发现的Bug,修复起来要花更多的时间。
芸芸:这对我们设计人员有什么用呢?好像都是一些细节的东西。
阿超:在我们写规格说明书(specification)的时候,要越详细越好,最好你的各项要求都可以表达成单元测试的一个测试用例。
芸芸:如果不能表示为一个单元测试呢?
二柱:那就是你写得还不够细。
小飞:我大胆地说一句。如果是一个人写写程序玩玩,单元测试似乎不那么重要。
二柱:你可以大胆地对你的女朋友说:“我们只是玩一玩……”看看效果如何。
阿超:如果玩一玩,什么都不太重要。如果你写的模块会有不同的人,在不同的时间使用,那你最好把你这一“单元”要做的事,以及它不能做的事,用单元测试清晰地表达出来。
2、好的单元测试的标准
下面我们讲讲怎样才算一个好的单元测试。
单元测试应该准确、快速地保证程序基本模块的正确性。下面是验证单元测试好坏的一系列标准:
单元测试应该在最低的功能/参数上验证程序的正确性。
单元测试应该测试程序中最基本的单元——如在C++/C#/Java 中的类,在此基础上,可以测试一些系统中最基本的功能点(这些功能点由几个基本类组成),从面向对象的设计原理出发,系统中最基本的功能点也应该由一个类及其方法来表现。单元测试要测试API中的每一个方法及每一个参数。
单元测试必须由最熟悉代码的人(程序的作者)来写。
代码的作者最了解代码的目的、特点和实现的局限性。所以,写单元测试没有比作者更适合的人选了。
问:如果我很忙,能不能让别人代劳做单元测试?
答:如果忙到连单元测试都没有时间做,那么你也没有时间写好这个功能。在一些极限编程的方法中,是可以考虑让别人来做单元测试的,但是,程序的作者还是要对单元测试负责。
最好是在设计的时候就写好单元测试,这样单元测试就能体现API的语义,如果没有单元测试,语义的准确性就不能得到保障,以后会产生歧义。
单元测试过后,机器状态保持不变。
这样就可以不断地运行单元测试,如果单元测试创建了临时的文件或目录,应该在Teardown阶段把这些临时的文件或目录删除。
如果单元测试在数据库中创建或修改了记录,那么也许要删除这些记录,或者每一个单元测试使用一个新的数据库,这样可以保证单元测试不受以前单元测试实例的干扰。
单元测试要快(一个测试运行时间是几秒钟,而不是几分钟)。
快,才能保证效率。因为一个软件中有几十个基本模块(类),每个模块又有几个方法,基本上我们要求一个类的测试要在几秒钟内完成。如果软件有相互独立的几个层次,那么在测试组中可以分类,如数据库层次、网络通信层次、客户逻辑层次和用户界面层次,可以分类运行测试,比如只修改了“用户界面”的代码,则只需运行“用户界面”的单元测试。