测试是贯穿整个整个软件工程的始末,做好测试对软件的质量会有一个质的保证,减少查找BUG的工作量,所以作为一个开发者需要了解各种测试流程以及核心思想,然而这次的单元测试是我们开发者必须要具备的技能,这次就让我们走进单元测试(Unit Test)!
1.1对单元测试的建议
①坚持的开发中编写单元测试,并把它培养成一种习惯! ②写出高效的单元测试,这种能力需要在实践中慢慢积累! ③提高对单元测试的认识高度,把它和编码工作同等对待!
1.2什么是单元测试?
通俗讲单元测试就是检查一个函数执行后它的返回结果或者它对系统数据造成的影响(或者其它方面的影响)是否跟你的期望一致,也就是为了证明代码的行为和我期望的一致!
1.3 我们为什么要使用单元测试?
①最直接的原因是保证我们函数的正确性,如果这个函数在没有保证正确的情况下就被上层代码调用,那么随着项目的深入,调用层次会越来越深,就很容易产生严重的BUG问题,从而增加开发难度,降低开发效率!
②根本原因是减轻我们开发人员的工作量,使我们的工作变的轻松(这只是一个相对说法)!
1.4 单元测试的内涵
如果把单元测试上升到一定程度后,它可以把我们的代码变的更加完美和简洁!
1.5 单元测试的本质
请记住一点,不是为工作而编写单元测试,单元测试是方便我们开发人员的,可以使我们的工作变的轻松!
单元测试可以减少我们花在解决不必要的BUG之上(并不是说没有BUG,而是说减少不必要的BUG),而把大量时间专注于业务需求上!
1.6 函数的行为和预期的一致吗?
如果测试只考虑在正确的环境下造成正确的影响,那么这样的单元测试是不及格的! 做测试就要考虑全面,各个方面都要涉及的到,如:环境因素(也可以是系统所处在的环境),各种异常,边界值等等,所以尽可能的考虑特殊情况,做到做到百密而无一疏(尽自己的最大努力达到)!
1.7 需要依赖单元测试吗?
答案是肯定的!
当你很自信的认为你写的函数是绝对正确的且没有测试代码做为依据的时候往往会出现意想不到的错误,因为你会疏忽了其它的一些情况,所以编写单元测试来保证我们函数的准确性是非常有必要的!
注:后面会说明测试应该测哪些情况!
1.8 单元测试干了什么,作用是什么?
①最直接的是保证了函数的正确性(这个大家都知道)!
②还有我们可以根据单元测试来判断此函数是用来干什么的,也就说单元测试类似于一个可执行文档,其它开发人员可以通过看单元测试就会明白你测试的函数是用来干嘛的! 1.9 如何进行单元测试?
这边我们使用的VS2008,2010自带的单元测试框架!
① 使用VS自带的Unit Test,简单易学! ② 测试要全面!
③ 保证所有测试都能通过,不管旧的还是新的测试代码,都要通过! ④ 保证所有测试没有对系统中任何模块产生影响(这个很重要)! ⑤ 及时运行测试代码,查看运行结果,保证系统的运行正常!
1.10 不要为没写测试代码找借口
一般情况的看来当你写的一个函数已经不太需要修改了,你就应该编写这个函数的单元测试代码!
大多数情况下开发人员的大量时间都是修改BUG,如果能尽早的做单元测试将会减轻你的工作量(虽然不可能没有BUG,但写Unit Test却对你是有益无害的),即使在以后的时间里出现BUG,我想你能很快的定位产生BUG的位置!
最后写Unit Test千万不要放在项目末期,如果此时写单元测试的人还是一个刚进项目新手的话,写单元测试就是扯淡,这样的方式是不能体现单元测试的核心观念的,而我就刚好处于这个状态,真的很后怕,所以还需要多多加油,努力了解系统流程!
1.11 如果真的没有时间写单元测试,请思考下面几个问题?
①对于你所编写的代码,你花在调试上面的时间有多少?
②你目前认为你的代码正确无比,但很有可能在系统中却存在严重隐患,你是否花了很多时间来查找这些隐患?
③对于一个新的BUG,你花了多长时间来定位这个BUG在源码中的位置? 总结:随着项目的深入,你的函数会被调用的越来越深,那么特殊情况就会经常发生,万一出现什么情况你将会耗费很多的精力来解决它,另一方面,适当的单元测试代码会很大程度上减少你的工作量,这是经过实践检验的!
2、测试需要从哪些方面着手
前言
笼统的来说测试条件无非就是两个方面:① 正向测试 ,② 反向测试!
如果单从这两个方面来思考,肯定出现丢三落四的情况,也就是说不全面,所以应该在上面两种情况的基础上再进行具体划分,那么只要我们能够遵循这些条件基本上就能做到全面(如果能做到,大约80%的问题应该都解决了),于是就出现了下面要说的六个方面内容! 前辈们把这些测试条件总结为:Right – BICEP 2.1 Right - 做正确的事,可以说是“正向测试”
这种测试前期任务是要准备足够的正确数据(前提是要保证数据的正确性,这个很重要),运行代码后返回的值或产生的影响是要跟自己的预期是一致的!
注意:如果准备的数据太大或容易丢失,建议把它放在数据文件中,然后让单元测试读取这个文件,这种方法会在下一篇会说到!
2.2 B - 边界条件(Boundary)
边界条件是测试里面的重中之重,必须要有足够的认识和重视! 而它又被分为七个方面的子条件,下面就让我们来一一熟悉它! ①一致性(Conformance)
数据是否符合我规定的格式(也可以说是非法字符吧)!
案例:比如我传入的参数文件名需要的格式是:文件名 + 日期(yy-mm-dd) + 扩展名,那么我就要写一个测试传入的文件名为 :“sa#$#$#$#”这样的格式! ②有序性(Ordering)
这方面主要是对涉及到数组和集合的数据,而且对数据的顺序有严格要求的函数,需要对它们里面数据的顺序进行测试!
比如:点菜系统菜谱中每道菜的顺序,或者去银行办理业务的排队系统等等! ③范围,区间性(Range)
值是否存在于一个最大值和一个最小值之间,主要是对值类型的数据做的测试! 这里面还有一个重要的测试点是 → 对数组,集合,以及Table,DataSet中的索引值进行测试,比如索引值不能为负,不能超出索引的范围等等情况!
比如:一个通过ID来搜索信息的函数,应该对这个ID进行最大值和最小值的测试!
④引用,耦合性(Reference)
这方面主要是:代码是否引用了一些不受本身代码控制的外部因素(比如:调用第三方接口,调用其它模块的接口等等)!
对于这些情况我们是没有办法控制的,所以在测试的时候只能模拟,而在模拟时我们会用到“Mole”技术,让它来帮助我们创建一个模拟环境(下一篇会介绍)!
比如:有的项目会调用银行接口,这种情况下只能先创造一个虚拟银行接口,然后再进行测试! ⑤存在性(Exist)
固定的测试,如Null,Empty,非零等等,这些都是必须考虑的! ⑥基数性(Cardinality)
对于这个测试说起来还是蛮难理解,这个测试只有在特定的场合下才会去考虑它!
它遵循一个原则:“0-1-N”! ⑦时间性(Timer)
对时间比较有依赖的软件或系统应该在这个方面着重测试!
主要考虑:事情是否按时间的顺序执行,是否在正确的时间执行,是否出现执行事情延误了!
相对时间:网站超时,数据更新超时等等! 绝对时间:不同的client间的时间是否同步! 并发问题在时间性测试中比较重要!
2.3 I - 反向关联(Inversion)
在准备数据或者验证数据时的一种反向思维,涉及到个人的思维方式问题了! 比如:有个函数对数据库进行了操作,但是它没任何返回值也没有任何提示,如果你是对正确的数据进行了测试,那么你要怎么知道测试结果跟你的预期一致呢,这里你就应该去查找数据库,看数据库里面的数据是否有真的改动,这就是一种反向的思维方式!
2.4 C - 交叉检查(Cross)
用一种数量检查另一种数量(需要考虑的情况不是很多)!
2.5 E - 强制产生错误(Error)
通过代码强制产生软件在运行过程中出现的特殊情况!
可以参考下面几种测试方面:内存耗光,磁盘用满,断电,正在执行更新数据时出现断网现象,网络负载严重导致瘫痪,系统时间出现导致和国际时间不一致等等一些情况!
2.6 P - 性能特性(Property)
性能测试工具的使用,没具体研究过性能测试工具,知道的朋友可以说下你们的经验!
进行压力测试,一点一点的加大数据量,10000条,100000条,1000000条这样进行压力测试!
总结:本人对反向关联和交叉检查这两个测试条件不是很理解,知道的朋友可以留言给我,我会把它补充到文章中去!
最后:下面是我对测试条件的小小总结,比较简陋!
3、实战单元测试
前两篇文章讲解了一些关于单元测试的基本理论知识,接下来我们应该理论联系实践,在实践中体会单元测试带给我们的便利! 环境:VS2008,2010版本!
关于怎么在VS中创建单元测试,园子里已经有很多这样的文章了,请参考以下链接: http://www.cnblogs.com/heqichang/archive/2011/09/30/2196779.html http://www.cnblogs.com/TerryFeng/archive/2009/05/24/1488333.html http://www.cnblogs.com/zhijianliutang/archive/2011/12/15/2288423.html
3.1 前言
一个完整的测试必须符合以下几点:
A) 考虑到各种情况,准备测试所需要的各种数据,这一步是测试的关键所在! B) 调用要测试的方法! C) 验证被测试方法的行为跟预期的是否一致!
D) 完成验证测试之后清理各种资源!
3.2 单元测试框架
测试框架的DLL文件名为: Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll
在VS自动创建单元测试项目就会自动引用这个DLL了! 测试类的基本结构如图:
基本的测试类结构就是这样的!
3.3 断言(Assert)
在单元测试代码里断言是无处不在的,我们应该合理的使用断言来验证数据! 它是一个静态类,主要有下面几种方法用来验证函数的结果跟我的预期是否一致! ① Assert.AreEqual
主要是验证函数产生的影响值或返回的值跟预期是否一致,这个方法不适合验证返回的数据集以及集合之类的数据,主要针对字符串,数字等等的单一类型,它还有个泛型重载,这个是比较好的,建议多使用,它还有第三个参数,是一个string型的message,基本上不会用到!
☆ Note:不要把参数的含义搞混,第一个参数是你的期望值(Except),第二个参数是函数返回或影响程序产生的实际值(Actual),不要把两个颠倒过来,因为如果运行正确没有什么大碍,如果运行产生错误,有的时候就会看错掉,造成判断失误,要注意了,这是一个规范! 例子:
Assert.AreEqual 没什么要讲的,情况跟上面相反,主要是验证实际值跟期望值不相等的情况! ③ Assert.AreSame 判断实际值跟预期值的类型是否一致! ④ Assert.AreNotSame 跟上面正好相反,测试类型不一致! ⑤ Assert.IsNotNull,Assert.IsNull,Assert.IsTrue,Assert.IsFalse 看这些方法名就知道什么意思了! ⑥ Assert.IsInstanceOfType,Assert.IsNotInstanceOfType 判断指定的对象是否是指定的类型! ⑦ Assert.Fail 迫使断言失败,不管前面的断言是否都成功了,但测试结果是错误的,因为我强制断言失败了! 总结:一个好的测试案例,里面的断言至少是大于一个的,这样才能验证数据的准确性,保证验证数据的严谨性! 例:如果实际值是个DataSet一般测试流程为:①判断是否为“null” → ②判断是否为“Empty”(验证是否有数据) →③ 接下来再验证数据的一致性,所以验证DataSet的基本流程就是这样的! 1 /// UpdateBondAssessApplicationConfiguration_ModifyData_DataUpdated() //注意命名规范,下一篇会着重讲解! 7 { 8 AssessApplicationConfigurationDataSet assessConfigurationDataSet = target.GetBondAssessApplicationConfiguration(); 9 assessConfigurationDataSet.CM_LookupConfiguration[0].Description = \"This is my modify\"; 10 AssessApplicationConfigurationDataSet actual = target.UpdateBondAssessApplicationConfiguration(assessConfigurationDataSet); 11 AssessApplicationConfigurationDataSet newAssessAppDataSet = target.GetBondAssessApplicationConfiguration(); 12 13 Assert.IsNotNull(actual); //第一步 验证是否为Null 14 Assert.IsTrue(actual.CM_LookupConfiguration.Rows.Count > 0); //第二步 验证是否为Empty 15 Assert.IsTrue(newAssessAppDataSet.CM_LookupConfiguration[0].Description == \"This is my modify\");//第三步 验证数据的一致性 16 Assert.IsTrue(CompareToTable(newAssessAppDataSet.CM_LookupConfiguration, actual.CM_LookupConfiguration)); 17 } 3.4 测试异常 → ExpectedException(异常属性) 当代码中有抛出异常的情况时,我们应该对这个异常的准确性进行测试,首先要捕获这个异常,然后再跟我预期定义的异常进行比较就行了! 在VS自带的测试框架中提供了处理异常的测试! 这个异常属性有两个构造函数重载: 第一个参数:函数中出现异常的类型 → [ExpectedException(typeof(NullReferenceException))] 第二个参数:异常所提示的信息(Message) → [ExpectedException(typeof(NullReferenceException),\"Don't is null.\")] 总结:在测试一些非法数据,边界值,异常测试是非常有用的,一旦发现异常,后面的一切断言和代码将跳过,然后系统将会把异常和你预期的异常进行比对,一致则表示通过,反之有错误! 1 /// 6 [ExpectedException(typeof(NullReferenceException))] //第一种只定义了异常类型! 7 public void UpdateLookupChequeNumberRegion_UpdateChequeNumberRegionAndDataSetIsNull_ThrowException() 8 { 9 LookupChequeNumberRegionDataSet actual = target.UpdateLookupChequeNumberRegion(null); 10 } 11 12 /// 14 /// 16 [RollBack()] //第二种定义了预期的异常类型还定义了异常信息! 17 [ExpectedException(typeof(BusinessException), \"The category and code combination you have entered already exist. Please enter a different category and code combination.\")] 18 public void UpdatePolicyConsideration_AddPolicyTheCODEIsSame_ThrowException() 19 { 20 string newGUID = Guid.NewGuid().ToString(); 21 CodeTableDataSet expectedDataSet = target.GetPolicyConsideration(); 22 expectedDataSet.T_IC_CODE.AddT_IC_CODERow(GetNewRow(expectedDataSet, null, ref newGUID)); 23 CodeTableDataSet actual = target.UpdatePolicyConsideration(expectedDataSet); 31 } 3.5 忽视测试 → Ignore属性 添加这个属性表明现在这个测试案例在运行时将不会被执行,跳过此方法! 1 [TestMethod()] 2 [Ignore()] //运行单元测试时将忽视这个测试案例 3 public void GetBondDebt_InputValidClientID_RecordFound() 4 { 5 int clientCoreID = GetClientIDForSomeCondition(); 6 DebtDataSet actual = target.GetBondDebt(clientCoreID); 7 8 Assert.IsNotNull(actual); 9 Assert.IsTrue(actual.Debt.Rows.Count > 0); 10 Assert.IsTrue(CompareToDataSetAndList(actual, clientCoreID)); 11 } 3.6 数据驱动测试 在上一篇提到过当你的数据量很大的时候,有一种解决方案是采用数据驱动测试,把我们需要用来测试的数据放在文件中,然后运行测试,让测试代码去读取文件中的数据! 其实它也有一定的局限性,所以在合理的场合中合理的使用将减轻我们的工作量,这个判断只能给为看官去判断了! 当前支持Sql Server ,Oracle,CSV,XML等等文件,下面我就介绍下CSV和XML文件的使用方法! 也可以参照园子里面的博文: http://www.cnblogs.com/zhijianliutang/archive/2011/12/15/2289398.html http://www.cnblogs.com/heqichang/archive/2011/10/08/2202441.html ①CSV作为数据文件 我们写一个简单不能再简单的的加法运算方法来作为示例: 1 public int Add(int numberOne, int numberTwo) 2 { 3 int one = numberOne; 4 int two = numberTwo; 5 int three = numberOne + numberTwo; 6 return three; 7 } a) 首先要创建连接字符串,具体步骤如下: I, II, III, 通过上面步骤的操作,就会进入选择文件的界面,按照提示即可完成,当然前提你的数据文件要准备好,完成之后会出现如下代码: 1 /// 3 /// 4 [DataSource(\"Microsoft.VisualStudio.TestTools.DataSource.CSV\", 5 \"|DataDirectory|\\\\number.csv\", 6 \"number#csv\", 7 DataAccessMethod.Sequential), //这个是连接字符串,你也可以手动写,不要按照上面步骤操作,当然要写对单词和“注意文件的路径”! 8 DeploymentItem(\"MyTest\\\\number.csv\"), 9 TestMethod()] 10 public void AddTest1() 11 { 12 Program target = new Program(); 13 int numberOne = Convert.ToInt32(this.testContextInstance.DataRow[\"One\"]); //获取数据! 14 int numberTwo = Convert.ToInt32(this.testContextInstance.DataRow[\"Two\"]); ; 15 int expected = Convert.ToInt32(this.testContextInstance.DataRow[\"Except\"]); 16 int actual = target.Add(numberOne, numberTwo); 17 Assert.AreEqual(expected, actual); 18 } 点击运行你的测试,程序会通过你设置的路径去文件里面一行一行的读取数据,然后验证数据,如果其中有一行数据报错,那么整个测试也就是失败了,所以保证数据的正确性很重要! 注意:CSV默认会以Excel的方式打开,但是它里面的数据摆放有一个规则,就是以逗号的形式呈现,所以我还是建议大家使用记事本来添加数据! ②使用XML作为数据文件 其实使用方法跟上面的一样,尤其注意的是你XML文件里面数据的格式! 1 3 8 11 注意:怎么来获取数据呢? Convert.ToInt32(this.testContextInstance.DataRow[\"price\"]) 通过这样的方式来获取数据,还有在选择文件时,要确认是否选中了一个数据源文件,这个不要疏忽了! ③使用数据库作为数据文件很简单,配置一下连接字符串,设置一下路径就好了,在此就不讲解了! 3.7 单元测试的利器 → “Mole”技术 首先想感谢下项目组的Tian Mi大哥是他把这项技术带给我们的,谢谢他的无私奉献! 园子里面也有一篇精华文章,讲解了基本用法,链接为:http://www.cnblogs.com/hwade/archive/2010/11/26/Moles.html “Mole”文件的下载地址: http://www.kuaipan.cn/index.php?ac=file&oid=29568238492847207 ①应用环境: 所属模块依赖于系统的其它模块,依赖于系统的一些配置环境,还有就是调用第三方接口或服务等等的场景! 在这样的场景下我们的测试是不能直接调用第三方接口或服务的,所以我们要制造一个虚拟的环境,当我们去调用接口时,Mole技术会拦截我们调用的方法,从而转向我们自己制造的虚拟环境,那么就不会直接调用第三方接口和服务了! ②基本的操作流程不讲解了,如果你看了 http://www.cnblogs.com/hwade/archive/2010/11/26/Moles.html这篇文章就明白了,我就将一些在写测试时遇到的几个小问题! ③如果“Moles”结束后,项目编译的时候报错,具体的错误我也不怎么知道了,但是我在网上搜了很久才找到答案的! 步骤:点击你的Moles文件,你会发现他是个XML配置文件,修改它的属性就好了,如: 1 2 3 ③有“Ref”参数的方法应该怎么写? 如果参数中ref参数,那么就不能按一般的方式写了,应该这样写: ④Moles的基本语法 Moles的内部原理没有明白,只懂的怎么去用它,基本的语法可以模仿上面的写法! 因为你把一个DLL Moles之后并编译之后会生成一个新的DLL,这里面就是虚拟环境场所,而且类名称都是以“M”开头的! 语法:命名空间 + Moles + M + 你Moles掉的类名 + Allinstance + 选择你要拦截的方法名 = (instance(必填参数,如果方法没参数,也必要添个)) => { //里面写你的逻辑,制造你自己的虚拟环境,如果方法有返回值,可以自己指定返回值的! }; 好了,关于在实践中的单元测试就这么多了,其实最重要的还是你考虑测试的角度要多样化,就是考虑问题要全面,还有代码的简洁性,重构,封装等等,下一篇将是对这方面的着重讨论! 4、单元测试背后的思考和感悟 4.1 好的单元测试应该具备的特点 一个好的单元测试一定有它具备的特点,下面就来说说那些主要的特点! 主要概括为 → A-TRIP原则: 自动化 → Automatic 彻底性 → Thorough 可重复性 → Repeatable 独立性 → Indepentdent 专业性 → Professional 恰到好处的单元测试会使你的工作轻松,代码整洁干净,乱用,没有准则的用会浪费你大量的时间,不但没有效果还会是工期延误,所有了解单元测试很重要! ① 自动化 a) 不需要人的参与,有的时候只是轻轻的点击一个按钮就能自动执行,所以自动化的标志是不能比点击一个按钮的过程还要复杂! b) 在签入其它的测试代码时不能对现有的代码造成影响! c) 能够自动识别测试是失败还是成功(VS2008以后的版本都集成了这个功能)! d) 在任何时候,任何地方都能自动运行(所以“Moles”技术就是关键)! 核心:执行测试代码和检查测试结果都必须自动化(VS2008以后版本都实现这个功能了)! 总结:I,不要引入一个由于需要手动步骤而打破单元测试的自动化模型的测试! II,对于测试所需要的任何条件(大部分是数据库)都应该让它成为自动化测试的一部分,如果有需要可以使用Mole技术! ② 彻底性 所谓的彻底性就是说你的测试案例必须要考虑的全面,应该把可能出现的问题都做成测试案例! 具体从哪些方面着手,可以参数这篇:走进单元测试二:测试需要从哪些方面着手 ③ 可重复性 a) 每个测试案例应该独立于所有的其它测试,而且必须独立于周围的(系统)环境! b) 测试代码能够一次又一次的运行,在不修改代码的前提下都能产生一样的结果,否则有BUG! c) 不要把测试代码写死,应该写的更加灵活一点,运用封装,重构等等的思想! d) 不要让测试本身也出现BUG,确保测试代码的正确性! ④独立的 a) 每个测试应该有很强的针对性,也就说一个测试只能测试一个方面的内容! b) 每个测试应该独立于环境(软件所处的系统环境)和其它测试! 总结:I,每个测试都不能够依赖于其它测试,你可以在任何时间运行这个测试而不受其它测试的影响,每一个测试都应该是一座孤岛! II,所以测试一个函数都有很多个测试方法,只有这样才是真正的测试! ⑤专业的 a) 所谓的专业就是你的测试代码应该跟你的开发代码保持一样的风格,如:简洁明了,封装,解耦,不要出现“Hard Core”,要灵活一点! b) 拒绝编写冗余的测试代码,千万要小心不要掉进这个陷阱,因为像我们这样的新手在初期都不会注意到这样的问题,所以我们要牢记在心里! c) 遵循普遍规则:1.维护封装 2.降低耦合! 总结:不管怎么样你都应该认认真真的对待单元测试,代码的质量要求都应该跟开发代码同等水平,这是作为开发者必备的素质! 4.2 单元测试的命名规范 在我们项目的中,可能需要测试的方法有成千上百个,而每一个测试方法都有可能写三个以上的测试案例,那么怎么来维护这么测试案例呢? 所以我们应该规范方法的命名方式,那么其他人在阅读你的测试代码时,直接通过方法名就能知道你的测试案例是测试哪个方面的了! Note:单元测试案例类似于一个可执行文档,可以帮助其它的开发人员了解方法的作用! 在我们的项目中是这样规定的:方法名 + _ + 你测试是哪个方面的内容 + _ + 产生的结果! 下面我就举个列子,下面的测试方法命名就是针对这个函数来命名的,如: 1 public DataSet GetDetails(int ID) 2 { 3 // 方法的作用:这个一个获取数据,并包装成一个DataSet的方法,传入的参数是一个bondAppID,那么我们怎么来设计案例和命名方法名呢? 5 } ①首先设计你的测试案例 看到这个方法我就会有这几个想法:1,最大值 2,最小值 3,刚刚好的值 4,随便一个值 5,还有的测试案例会随着你代码中的逻辑而产生! 下面是我的测试案例以及方法名的命名,测试方法是上面的那个: 16 /// 17 /// Input valid bond ,but the cheque is presented Status. 18 /// 19 [TestMethod()] //如果你预期有数据返回,那么就应该在最后面加上“RecordFound”,这样别人看的时候就能一目了然了! 20 public void GetDetails_CheckPresentedStatus_RecordFound() 21 { 22 //To Do. 30 } 31 32 /// 33 ///Cancel Status. 34 /// 36 public void GetDetails_CheckCancelStatus_RecordFound() 37 { 38 //To Do. 45 } 46 47 /// 51 public void GetDetails_CheckDetailsByMaxBoundaryValue_NoRecordFound() 52 { 53 //To Do. 59 } 60 61 /// 65 public void GetDetails_CheckDetailsByMinBoundaryValue_NoRecordFound() 66 { 67 //To DO. 73 } 74 75 /// 79 public void GetDetails_CheckDetailsByAnyBondID_NoRecordFound() 80 { 81 //To Do. 89 } ② 说一下总的命名规范 a) 如果返回值是“Bool”型的话,应该在最后面加上“RetrunTrue”或“ReturnFalse”! 如:CheckRebateHasNewChange_ExistNewChanges_ReturnTrue b) 如果返回值是集合或者DataSet之类的类型话,应该在最后面加上“RecordFound”或者“NoRecordFound”! 如:GetPurchaseOrder_ByPurchaseOrderId_RecordFound c) 如果是测试异常的话,应该在后面加上“ThrowException”! 如:CreateRebateApplication_ExistSameRebateID_ThrowException d) 如果是岁数据库进行删除或者更新,插入的操作时,应该在后面写上“DataUpdated”或“NoDataUpdated”! 如:UpdateClientIncome_UpdateIncome_DataUpdated 总结:基本的变动部分都是你中间的描述,中间的描述尽量做到简洁明了,应该以动词开头,如:Update,Input,等等! 总结:好的命名方式可以增强代码的可读性,尤其是类似于单元测试这样的可读性文档,应该更加注意这方面的考虑! 4.3 建立自己的公共调用库 一个好的单元测试应该都有自己的公共调用库,这样能减少很多的冗余代码,使的代码简洁易懂! 所以建立了公共库的单元测试符合了单元测试特点中的专业性 → 1.维护封装,解耦等等特点! ①建立数据库访问库 测试中的准备数据从何而来,有的时候数据准备比较简单,那么如果传入的参数是DataSet的呢,里面的数据比较多呢,或者更新数据库呢,我们应该怎么做? 那么我们应该去数据库中查找数据,然后在组合到DataSet中去,所以建立必要的数据库访问类很重要! ②建立自己的公共库 这些是为了你验证数据,或者搭建环境等等的一些用处,提高编写测试代码的速度! 如:,这是我们建立的公共调用库,所 以如果建立了库,对于我们的单元测试将是大有裨益的! 4.4 单元测试带给我的思考和感悟 ①感悟 说实话从一开始做单元测试的时候,我对它真的很鄙视,我觉得它的含金量很少,编写单元测试代码太枯燥了,导致了那时候我的心态是多么的浮躁,以至于在思想方面出现了偏差,感兴趣的朋友可以看看这篇文章 → 工作的思考,是走还是留! 经过一段时间的自我反省,也确实让我慢慢的走上了正轨,感兴趣的朋友可以看看 → 迷茫后的感悟! a) 做任何一件事都应该专心,戒骄戒躁,这是我深有体会的! b) 事无大小,不要认为你的事很无聊,很枯燥,很不值得一提,但是一旦你做好做精之后你就发现原来你不知道的还有这么多啊! c) 努力 + 学习方法 + 工作态度 → 是我这段时间感悟比较深的一件事! ②思考 前提:我们做的单元测试是在项目后期写,而且我对我负责的模块是一窍不通的(我是刚刚进这个项目组的),业务流程根本不懂! 在这样的前提下,每天先熟悉下流程,看一下代码,找带头大哥帮我讲解讲解,然后才开始写单元测试代码,由此我有了下面三点的思考: a) 单元测试不应该在后期做,应该在项目的开发时期去完成它,这个可能跟我们的项目本身有原因把! b) 对于找一个还不懂业务流程的人来做单元测试自我感觉是有点不合理的,至少会花费更多的时间来熟悉流程。然后再做单元测试! c) 项目过程中的代码编写习惯也是很重要的,这是我应该要加强和思考的,也是我需要培养的习惯! 4.5 总结图示 因篇幅问题不能全部显示,请点此查看更多更全内容 //一定要设置根节点,如果不设置在文件选择的时候会报错! 2