单元测试之道(使用NUnit)

  首先来看下面几个场景你是否熟悉

  1、你正在开发一个系统,你不断地编码-编译-调试-编码-编译-调试……终于,你负责的功能模块从上到下全部完成且编译通过!你长出一口气,怀着激动而又忐忑的心情点击界面上的按钮,顿时你刚刚的轻松感烟消云散:系统无法正常工作,你想读的数据显示不出来,你想存的东西也送不到数据库……于是,你再次回到IDE里,设断点、调试、一层一层跟踪,当你精疲力尽终于将数据送到数据库里,你又发现了其它问题,于是你继续设断点、调试、编译、调试……

  2、你狂躁地敲击着键盘和鼠标,咒骂着不断出现的bug:啊?这里怎么没返回值啊!哎?这里不该是0啊!不对啊,这里怎么没数据……你永远不知道还有多少bug,你也永远不知道你的改动会不会引入其它bug——这里有几十个甚至上百个类,几百几千个方法!我不能都照顾到啊!你感觉bugs像敲击鼹鼠游戏中的鼹鼠:打下了这个,另一个又从其它洞口露出头来……

  3、也许是毕业答辩的演示,也许是客户的审查,你小心地打开自己要演示的系统,进行着预定的操作,忽然,有个功能不能正常运行,你大汗淋漓,在答辩老师或者客户质疑且不满的目光下你试了又试,但还是于事无补……于是,答辩老师可能扭头便走,客户可能愤然离去,然后离去的还有你的学位证和项目奖金。当后来你检查代码时,发现这一切竟然只是因为一个底层工具类中一个方法输出结果为空。

  如果你觉得上面的场景令你似曾相识甚至痛心疾首,那么你应该看完这篇文章。

  什么是单元测试

单元测试(Unit Test)的一个测试用例(Test Case)是一小段代码,这段代码用于测试一个小的程序功能(一般是一个方法或相关的几个方法)行为是否正常。下面给出一个实际项目中单元测试用例的代码,大家可以不用深究这段代码中的细节,这里贴这段代码只是给大家一个直观的感觉。

1 ///

2 /// 测试基本的添加及删除角色是否正确

3 ///

4 [Test]

5 public void TestAddAndRemoveRole()

6 {

7 IRoleServices roleServ = UnityHelper.CreateContainer().Resolve<IRoleServices>();

8 IRoleRepository roleRep = UnityHelper.CreateContainer().Resolve<IRoleRepository>();

9 Assert.IsNotNull(roleServ);

10 Assert.IsNotNull(roleRep);

11

12 String timeStamp = DateTime.Now.ToString();

13 RoleDto newRole = new RoleDto()

14 {

15 Name = "测试角色" + timeStamp,

16 Desciption = "此角色仅供测试使用",

17 };

18 roleServ.AddRole(newRole);

19

20 RoleDto addedRole = roleRep.GetRoleByName("测试角色" + timeStamp);

21 Assert.AreNotEqual(-1, addedRole.ID);//确认新角色添加成功

22

23 roleServ.RemoveRole(addedRole.ID);

24 Assert.AreEqual(-1, roleRep.GetRoleByName("测试角色" + timeStamp).ID);//确认刚才添加的角色删除成功

25 }

上面的Unit Test Case来自我目前负责的一个项目,这段代码的作用是测试Services层对角色的添加和删除是否正常工作。这里大家可以先不必太细究代码,后面会有详解。

  为什么大家不使用单元测试

按照惯例,说完什么是单元测试,就该说为什么要使用单元测试了。但是,我在这里想先和大家讨论,为什么很多开发人员知道单元测试,也“认为”单元测试有必要,但绝大多数开发人员都不写单元测试,能认真对待单元测试的开发人员更是寥寥无几了。

我私下调查了一些开发人员,发现大家不写单元测试主要有两点原因:一是对单元测试存在很多误解,二是没有真正意识到单元测试的收益。下面我就这两点做一些讨论。

首先,我们来看看大家对单元测试普遍存在哪些误解。

误解1:单元测试属于测试工作,应该由测试人员来完成,所以单元测试不属于开发人员的职责范围。

正解1:单元测试虽然叫做“测试”,但实际属于开发范畴,应该由开发人员来做。

在大多数开发人员眼里,“开发”和“测试”是两个泾渭分明的范畴,他们认为:开发人员的工作就是写新代码,实现新功能,至于代码的测试,那是测试人员的职责,我只要让代码编译通过就行了。

我们都知道,软件是很复杂很抽象的东西,软件开发人员压力都很大,况且人非圣贤,强求开发人员开发出没有缺陷的程序是不现实的,所以才有了“测试工程师”这一职位。但是,开发人员至少应该保证一点:你写的每一个函数或方法(Function)应该能够正常完成功能,即行为正常。软件最终可能会有缺陷,这不是开发人员完全可以控制的,但你写了一个类,类里有4个方法,作为开发人员应该保证这四个方法实现了“眼下”的功能。例如,你写了一个获取IoC容器的工具类,你总要保证其中的GetContainer方法能正确返回一个Container吧。

所以,单元测试虽然叫“测试”,但实际其属于开发范畴,其目的是保证开发的功能子项能完成正确实现其基本功能。甚至我个人认为,当开发人员开发每一个功能子项(通常是方法)时,如果不能附带一配套的单元测试代码,都不能算开发完成。换言之,单元测试代码应该是开发人员必须提供的要素。

误解2:单元测试是一种测试,其功能是对代码进行检测。

正解2:单元测试是一种工具,其功能除了是对代码进行检测,更重要的是对软件的质量起到一种保证,并且是为他人和后续编码、重构工作提供的一种十分美妙的工具!

单元测试不是一种测试。没错,我不是在说疯话,单元测试其实是一种工具。特别是当自动化测试软件(如NUnit、JUnit)出现后,单元测试更像是一种工具了。

当你处在一个多人开发团队中,你需要和其他队友配合开发,而这在程序层面则表现为你开发的Class会被别人用,而你也会用别人开发的Class。我们每个人都希望别人交给我的Class是行为正确的,如果我拿到一个同事写的数据库操作类DBHelper,但发现其中的Connect方法根本无法连接上数据库(虽然没有编译错误),那我将非常郁闷。所以,在交给别人一个Class之前,你应该使用Unit Test保证这个Class是正常实现功能的,在交付的时候,你应该一手递上刚出炉的Class,然后另一只手递上配套的Unit Test,然后说:嘿!哥们,这是你要的类,而这个是配套的单元测试,你可以随时使用自动化测试工具运行它以便迅速知道这个类是否工作正常。

这将会是个很棒的工具,你的队友以后可能会想知道它的改动是否影响了你提供的类的功能,也可能会对你的类进行重构,但无论何时,它只要拿出你的配套单元测试,让自动化测试工具跑一下,不出几秒,就知道你提供的类是否还正常完成功能。即使是对于你自己,以后也会有很多机会用到它。而当你写的代码出现bug,你可以拿出你这段代码调用的所有类的单元测试跑一遍,很快就能知道到底是你依赖的类出了问题还是你自己代码有问题,而不必抓狂似地到处设断点。

误解3:项目经理或技术主管没有要求写单元测试,所以不用写。

正解3:写单元测试应该成为开发人员的一种本能,开发本身就应该包含些单元测试。

就像项目经理不用告诉你要使用计算机写程序一样,写单元测试应该成为开发人员的必须动作。因为你是开发人员,因为你在做开发,所以你必须写单元测试,就这么简单。

误解4:写单元测试获益者是测试人员,而开发人员无法从中获益,还要搭上宝贵的时间。

正解4:写单元测试谁都获得不了像开发人员获得的那么大的益处。

有了单元测试,你可以随时从同事手中接过值得信赖的代码;有了单元测试,你可以随时保证你写的代码行为正确;有了单元测试,你可以随时通过自动化操作得知某个Class行为是否正确;有了单元测试,你以后的Debug和重构工作将变得轻松异常;有了单元测试,……没有人比开发人员从中获得的利益更大了。

  为什么开发人员很难意识到单元测试的收益

关于这点,我认为有两个重要原因。

第一、绝大多数开发人员没有尝试过贯彻单元测试。

这个很好理解,如果你不亲口尝尝一道菜,即使是海参鲍鱼,你也不知道它有多美味。我曾经也是其中的一员,但当我第一次将单元测试贯彻于项目中并尝到甜头后,我就爱上“她”了,所以,迈出一地步,很关键。

第二、人有一种天性:相比长远的更大利益,人们更倾向于眼前的小的多的利益,正所谓“贪小便宜吃大亏”是也。

想起了美国人类行为专家的一个实验:他到了美国一个小学,里面一个一年级班级有48个孩子,他给每个孩子5颗做了特殊标记的糖,并告诉他们如果到一周后谁能一颗都不吃,我就给他100颗糖。一周后,48个孩子中只有4个孩子做到了。他跟踪了这48个孩子30年的成长,最后发现那4个孩子都成为了十分成功的人物,他们4个人30年后拥有的财富是剩下44个孩子财富总和的3倍。

同样道理,即使很多开发人员也知道好的单元测试能让以后省不少心,但他们也宁可省掉写单元测试时间去堆砌代码。因为我们总觉得今天省掉1个小时多写一个类更有的赚,虽然我们以后要为省掉的1小时多付出3个小时去抓狂。

  小结一下

上面写了很多,所以我认为这里有必要小结一下,整理一下思绪。

单元测试的概念——一小段代码,用于检查一个或几个相关的方法行为是否正确。

单元测试的本质——随功能代码一起提供的一个配套工具。

单元测试的用途——保证交付Class行为正确,随时可用于自动化检测其对应的Class行为是否正确,对整个软件的质量是一种保证,对缺陷是一种控制。

  为什么需要单元测试

我忽然发现,写了上面的文字后,再来讨论这个问题有些多余了,那么我尽量写简短一点。

1、开发人员有义务提供行为正确的Class,也有权利得到行为正确的Class。

很明显,如果你和你的同事,都能重视单元测试的话,你将同时履行这份义务和享受这份权利。

2、尽早消灭缺陷。

缺陷越早消灭所付出的代价越小,而越往后其代价呈指数增长,这是有充分的实验数据证明的,并已经被写到每一本软件工程教科书中。毫无疑问,当你交付一个Class前,就将其行为上的缺陷全部扼杀,那将取得巨大的收益。

3、使合作变得愉快顺畅。

想想看,每个你调用的Class,都是经过你的同事测试,确保行为是正确的,这是多么美妙的事情!我们写程序经常没有安全感,我们战战兢兢,很大程度上是因为我们没信心认为调用的每个Class行为是正确的。

4、得到一个有力的工具,会在后续工作中大显身手的工具。

如果每个Class都有配套的单元测试,好的,如果你想确认你的改动有没有影响到其它几个Class,run it!如果你想看看你调用的类是否行为正确,run it!如果你在重构,想看看重构有没有改变或损害其行为,run it!你正在调试一个bug但很难定位问题出在哪个地方,run it!你想看看目前项目中所有集成进来的代码是否行为都正确 run it! ……

  .NET平台下使用NUnit进行单元测试的实例

如果你愿意,你可以手工设计和运行单元测试,但这是低效和让人恐惧的。目前,各个平台上都有较为流行的自动化单元测试工具,像Visual Studio 2008本身就集成了单元测试功能。但是,我更愿和大家分享的是一个叫NUnit的工具。其官方网站为:http://www.nunit.org/。这是一个开源且免费的.NET平台下自动化单元测试工具,可以在其官网下载。NUnit是XUnit家族的一员,其体积小巧,使用简单,但功能强大,一直是我做单元测试的首选。

另外,这个例子我选取目前我正负责的一个实际项目,这是一个国家863项目。这个项目使用了敏捷开发方法,贯彻了以保证为目的的测试驱动、持续集成等实践。我将截取其中几个片段和大家分享一下单元测试的一些实践。

值得一提的是,我对这个项目的单元测试要求是相对严格的,我们使用的配置管理工具是SVN,作为项目负责人,我对所有开发人员有一个要求:每一个新开发的Class,必须有配套的单元测试,并且在每次Commit到SVN前,不仅仅要保证Commit的代码编译没问题,还要跑通所有单元测试,否则不准Commit到SVN。这就保证了每个人Update到的Class都是行为正确的。再配合面向接口编程方法和Mock技术,大大提高了代码的可测试性,使得开发过程一直比较让人满意。刚开始大家觉得我的要求有些过分,但是当每次结合时几乎都没有出现问题,每次刚刚集成的新功能都能顺利在UI上跑通,大家也就慢慢接受了,并且渐渐都对待单元测试非常认真。

这个项目的整体结构大家可以先看一下,其中XUnit项目就是单元测试项目。

图1、解决方案结构图

其中的单元测试Case进行了一定的组织,相应工程的Case放在了不同目录下。当然,这个大家可以根据具体情况自行确定组织方式。

  对UnityHelper的单元测试

这个项目选用的依赖注入工具为Unity(http://www.codeplex.com/unity)。因为获取UnityContainer是一个常用操作,所以我将其封装成一个辅助类,代码如下:

1 /**********************************************************************

2 *

3 * 北京航空航天大学计算机学院软件工程研究所 All Rights Reservd

4 *

5 * 任何拷贝都不允许删除此处
版权声明

6 *

7 * 作者:张洋

8 *

9 * 建立时间:2010-01-08

10 *

11 * ********************************************************************/

12

13 using System.Configuration;

14

15 using Microsoft.Practices.Unity;

16 using Microsoft.Practices.Unity.Configuration;

17

18 namespace SPMS.Common.Utils

19 {

20 ///

21 /// 工具类

22 /// 封装了Unity中Container的创
建工作,并保证Unity Container的单例性

23 ///

24 public class UnityHelper

25 {

26 private static IUnityContainer _container = null;

27

28 ///

29 /// 获取Unity Container

30 ///

31 /// 全局唯一的Unity Container实例

32 public static IUnityContainer CreateContainer()

33 {

34 if (null == _container)

35 {

36 _container = new UnityContainer();

37

38 //从配置文件中读取IoC配置信息

39 ExeConfigurationFileMap map = new ExeConfigurationFileMap();

40 map.ExeConfigFilename = "unity.cfg.xml";

41 Configuration config = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.
None);

42

43 //通过配置信息初始化Container

44 UnityConfigurationSection section = config.GetSection("unity") as UnityConfigurationSection;

45 section.Containers["defaultContainer"].Configure(_container);

46 }

47

48 return _container;

49 }

50 }

51 }

这段代码只有一个方法,就是CreateContainer,其作用是获取全局唯一的UnityContainer实例。在写完这个代码后,我开始写单元测试,我能想到有四个点要测:

1)能正确返回UnityContainer

2)返回的UnityContainer能正确创建对象

3)保证创建的UnityContainer是单例的,即全局唯一实例

4)返回的UnityContainer在创建配置为单例的对象时,返回的对象应该是单例的

有了这四点想法,我写了如下的单元测试:

1 /**********************************************************************

2 *

3 * 北京航空航天大学计算机学院软件工程研究所 All Rights Reservd

4 *

5 * 任何拷贝都不允许删除此处版权声明

6 *

7 * 作者:张洋

8 *

9 * 建立时间:2010-01-08

10 *

11 * ********************************************************************/

12

13 using NUnit.Framework;

14

15 using SPMS.Common.Utils;

16 using SPMS.Repository.IRepository;

17 using SPMS.Services.IServices;

18

19 namespace SPMS.XUnit.CommonTests

20 {

21 ///

22 /// UnityHelper的单元测试类

23 ///

24 [TestFixture]

25 public class UnityHelperTests

26 {

27 ///

28 /// 测试获取Container是否正常

29 ///

30 [Test]

31 public void TestCreateUnityContainer()

32 {

33 Assert.IsNotNull(UnityHelper.CreateContainer());

34 Assert.IsInstanceOf(typeof(Microsoft.Practices.Unity.IUnityContainer), UnityHelper.CreateContainer());

35 }

36

37 ///

38 /// 测试Container创建对象是否正常

39 ///

40 [Test]

41 public void TestCreateObject()

42 {

43 IRoleRepository roleRepository = UnityHelper.CreateContainer().Resolve<IRoleRepository>();

44 Assert.IsNotNull(roleRepository);

45 Assert.IsInstanceOf(typeof(SPMS.Repository.NHibernateRepository.NHRoleRepository), roleRepository);

46

47 IRoleServices roleServ = UnityHelper.CreateContainer().Resolve<IRoleServices>();

48 Assert.IsNotNull(roleServ);

49 Assert.IsInstanceOf(typeof(SPMS.Services.ServicesImpls.RoleServicesImpl), roleServ);

50 }

51

52 ///

53 /// 测试Container是否是单例对象

54 ///

55 [Test]

56 public void TestSingletonContainer()

57 {

58 Assert.AreSame(UnityHelper.CreateContainer(), UnityHelper.CreateContainer());

59 }

60

61 ///

62 /// 测试指定为Singleton的实例,是否为单例对象

63

时间: 2024-09-20 04:06:17

单元测试之道(使用NUnit)的相关文章

艾伟_转载:单元测试之道(使用NUnit)

首先来看下面几个场景你是否熟悉 1.你正在开发一个系统,你不断地编码-编译-调试-编码-编译-调试--终于,你负责的功能模块从上到下全部完成且编译通过!你长出一口气,怀着激动而又忐忑的心情点击界面上的按钮,顿时你刚刚的轻松感烟消云散:系统无法正常工作,你想读的数据显示不出来,你想存的东西也送不到数据库--于是,你再次回到IDE里,设断点.调试.一层一层跟踪,当你精疲力尽终于将数据送到数据库里,你又发现了其它问题,于是你继续设断点.调试.编译.调试-- 2.你狂躁地敲击着键盘和鼠标,咒骂着不断出现

《Google软件测试之道》—第2章2.1节SET的工作

第2章 软件测试开发工程师 Google软件测试之道 C:\Documents and Settings\Administrator\桌面\页面提取自- 9780321803023_book.jpg 在理想情况下,一个完美的开发过程是怎样进行的呢?测试先行,在一行代码都没有真正编写之前,一个开发人员就会去思考如何测试他即将编写的代码.他会设计一些边界场景的测试用例,数据取值范围从极大到极小.导致循环语句超出限制范围的情况,另外还会考虑很多其他的极端情况.这些测试代码会作为产品代码的一部分,以自检

《Google软件测试之道》—第2章2.2节测试认证

本节书摘来自异步社区<Google软件测试之道>一书中的第2章2.2节测试认证,作者[美]James Whittaker , Jason Arbon , Jeff Carollo,更多章节 2.2 测试认证 Patrick Copeland在本书的序中强调了让开发人员参与测试的难度.招聘到技术能力强的测试人员只是刚刚开始的第一步,我们依然需要开发人员参与进来一起做测试.其中我们使用的一个 关键方法就是被称为"测试认证"(译注:Test Certified)的计划.现在回过头

《Google软件测试之道》目录—导读

内容提要 Google软件测试之道 每天,Google都要测试和发布数百万个源文件.亿万行的代码.数以亿计的构建动作会触发几百万次的自动化测试,并在好几十万个浏览器实例上执行.面对这些看似不可能完成的任务,谷歌是如何测试的呢? 本书从内部视角告诉你这个世界上知名的互联网公司是如何应对21世纪软件测试的独特挑战的.本书抓住了Google做测试的本质,抓住了Google测试这个时代最复杂软件的精华.本书描述了测试解决方案,揭示了测试架构是如何设计.实现和运行的,介绍了软件测试工程师的角色:讲解了技术

微软的软件测试工程师——《微软的软件测试之道》

   好多人极力推荐<微软的软件测试之道>这本书,于是在网上搜索了一番,英文版的阅读起来有难度,在51CTO上发现了前第二章和第三章中文的内容.     在这个世界顶级的企业里,软件测试工程是的测试是怎样的. ------------------------------------------------------------------------------------------------      一.职位名称含义: 即使你给玫瑰花起不同的名字,它闻起来可能还是同样的香.但是,如果

单元测试之白盒测试方法——代码审查

我所在公司内目前还没有单元测试,前两天测试某系统的FTP上传功能时,发现其软件的流程设计有问题,进而觉得单元测试对系统还是很重要的,今天又在网上查看了很多关于单元测试的文章,发现现在做单元测试的公司还真的不是很多呀.原因之一单元测试的bug发现率太低使得公司忽视了这一块:再就是公司内没有一个好的单元测试流程.鉴于上面提到的两个原因及公司现在的环境(流程的可行性),我想出了以下的白盒测试流程.简称单元测试之白盒测试方法(代码审查). 首先先说一下测试中需要出的文档. 在单元测试前可以进行代码规范性

测试之道--阿里巴巴八年测试专家倾情奉献

一.  前言 我从事测试工作将近八年了,从起初的不懂测试,怀疑测试,到相信测试,再到坚定测试,其中经历的辛酸.煎熬无法言表.在从事测试工作的这八年里,有人质疑,也有人追捧,唇枪舌剑,没完没了,貌似测试永远都是个站在舆论风口浪尖的角色.本文乃在下之精血所作,是我对测试的高度概括,旨在帮助大家了解测试,新人可以更好地从事测试工作,老人可以进行测试探讨,交流思想.为了尽量让更多的人理解测试,本文重在述道,少说测试之术,相信看完之后,各位自有论断,功过是非留于各位看官说. 二.  测试的万能模型 为什么

python单元测试之unittest

现在单元测试也要慢慢作正规了. 参考以下贴子作了一次python, http://www.cnblogs.com/sunshine-blog/p/6735690.html 最终我可是要用django和mock的哟. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ python unittest四大组成"元件": test fixture(测试脚手架)--测试代码的运行环境,指测试准备前和执行后要做的工作,包括setUp()和tearDown(): TestCase(测试

Visual Studio 2012 单元测试之泛型类(Generics Unit Test)

关于单元测试,如果不会用可以参照我的上篇博文----在Visual Studio 2012使用单元测试 首 先分享一篇博文,[Visual Studio] 开启Visual Studio 2012通过右键菜单创建单元测试(Unit Test). 泛型有两种,一般泛型与类型约束泛型,在对包含泛型的方法进行单元测试中也可以这么分,详情可 参阅http://msdn.microsoft.com/en-us/library/vstudio/ms243401.aspx  .从该页面可以知道,关于 泛型的单