小皮球的故事 发表于 2017-6-1 16:40:03

单元测试的实施

 一、单元测试的介绍
  1、单元测试和白盒测试
  要回答这个问题,需要从测试的分类谈起,见图1. 软件测试的分类。
  在单元测试阶段,那时候可能软件的界面还没有做出来,测试工程师自然就没有办法去做用户视角的测试,常会针对代码进行测试,久而久之,单元测试和白盒测试就分得不是那么清楚了。作为测试人,我们应该知道单元测试只是测试阶段中的一个过程,而白盒测试是针对代码进行测试。
  
图1. 软件测试的分类
  2、"单元"是什么?
  个人认为,单元测试阶段的测试对象,是比较灵活的,但不管怎么灵活,单元测试的颗粒度都应该是最小的。
  若是软件设计的耦合性比较低,那么在面向过程的语言,那单元应该就是某个过程或者函数,而面向对象的程序设计中,单元就是一个个的类。
  反之,若是软件设计的耦合性比较高,你把完成了某一个功能所涉及到的几个函数或者一个package中的类说成是一个单元,也没什么不可以。
  3、单元测试这个事情,我们总觉得让开发做比较合适,但是你若是很牛X的测试工程师,交给你做也是可能的。
  二、前期的单元测试工作内容
  1、计划
  有计划才能有条不紊的做事。
  单元测试也不例外,需要计划,但是什么时候做单元测试计划呢?
  计划中有些什么呢?
  单元测试的要是开展的话,测试入口是什么?又测试到什么时候结束呢?
  1)什么时候写计划
  单元测试的测试依据,应该是详细设计,很多人觉得现在做项目,哪里看得到详细设计,开发人员能够把代码按照时间点写出来就不错了,都顾不上设计。但是,我还是要说,单元测试的依据就是在编码之前对"程序的"设计",我们不要去关注这个"设计"是以什么样的形式存在,并不一定非要一个多么正式的文档,也许,它只是个Excel,或者是思维导图,甚至就只是一些图片,只有内容上写了每个包作用,每个包中包含的文件是完成了什么功能,相互之间如何传递接口数据,每个文件中的功能是怎么完成的,这就算是详设。这些内容也许在开发人员工作中,不是集中一段时间完成的,今天开发一组开会讨论了某个业务,明天开发二组又商量了另一个业务,那么作为项目的负责人,要去整理收集这些信息,当然,整理这些信息的人,极有可能是身为测试工程师的你。
  谈完了测试的依据-详设,那么单元测试何时被计划的答案就很容易得到了。就在详设完成了以后开始计划单元测试。
  有个问题是这样的"单元测试传递参数边界如何界定?单元测试测试函数时,怎么知道是不是需要测试:传递参数不允许为null 的情况 (1.函数注释里没写明,2.去问开发人员不知道,3.不知道哪些功能调用该函数)",我看了很久这个问题,猜想提问者可能是想表达当一个被测试的函数有好几个需要传递的参数,但是因为没有这几个参数的说明,传递参数不晓得怎么构造,不晓得我是否准确的理解了提问者的问题。若使我理解的那样的话,我真心觉得,最好的方法是去找知情者沟通,笨办法就是自己坐在工位上瞎猜,这是句玩笑话,我想表达的意思是,单元测试的依据就是详设,那么详设中一定会有对参数的说明,若是搞不清这些最基本的信息,工作是无法开展的。
  2)计划中包含什么
  i.测试的范围:
  · 一般最常见的的是从功能角度考虑,确定测试的是哪些功能模块对应的"单元",即对应的类或者方法
  · 也有可能是在项目中遇到算法比较复杂的部分,比如群红包分配的算法
  · 也有按照测试的类型,不仅是单元级别的功能测试,还比如有单元级别的性能测试、单元级别的安全检查
  ii.测试的环境资源:看需要哪些硬件和软件的设备,好提前准备
  iii.测试的人员:因为单元测试对测试工程师的要求是需要良好的代码能力,所以需要看看团队中who can,就who up。再一个,你可能会认为单元测试不是都是开发做吗?其实单元测试是细颗粒度的测试,有些要针对代码测试,有些也要针对界面测试,开发和测试一起,大家分工合理,才能做好单元测试
  iv.测试的入口:单元测试要进行之前,必须的入口需要定义,比如,是不是做好的详设,是不是准备好的资源、用例设计是否已经完成、是不是确定好开发人员和测试人员的工作界限。这里不给大家定死入口的标准,因为毕竟每家企业都有自己的特点,每个项目也不一样
  v.测试的出口:在计划中首先就确定单元测试的质量目标,比如"某等级的缺陷不超过3个"这样的句子。
  vi.测试进度的安排:确定某年某月某日某人要完成某些单元的测试,确定该项工作的实施人和检查者。
  vii.风险:是进行单元测试的过程中预料到的风险,并提出风险的解决措施。
  总之,计划就是定下做单元测试的人力资源、物力资源、时间安排、入口和出口。
  至于单元测试具体的工作要怎么开展,我们可以讨论一下单元测试的方案。
  2、方案
  很多人提出的问题是,怎么做好单元测试?怎么开展单元测试?这类问题比较开放性,真不好按照某种套路回答。这种问题,我想本身也没有标准答案,我只能说说我的看法。
  怎么做单元测试,这就是要确定方案
  那么方案中,要明确哪些内容呢?
  i.测哪些,不测哪些,说清楚了后,要说明原因
  ii.测试的那些,哪些重点测,哪些次重点测,要说明原因,当然,重要程度的划分可以不止重要和不重要两个等级,完全可以根据项目的规模、难度划分多个等级
  iii.重点测试和次重点测试,分别采用什么测试方式(手工、自动化)、采取哪种用例设计方法、分别要测试的人员和回归的次数,总之重点测试的部分,在人力物力和时间上肯定是倾斜的
  iv.若是手工测试,那么确定一下用例的设计模板,用例的提交地址、用例怎么评审、缺陷怎么记录,记录时的规范用、语
  v.若是自动化测试,除了要确定手工测试的那些内容之外,还要考虑用哪一款工具,测试的框架要在团队内部统一。而且自动化测试经常还要开发人员的源码融合进来,所以尽可能的要考虑测试框架和开发框架不要有违和感,莫让大家费劲的做单元测试,所以一个好的持续集成方案,也是要在单元测试方案中体现的。
  说明一点:有些测试工程师会说自己的公司里根本就没有提出过方案,看都没看到过这种文档,动手测就是了;其实,我们不应该拘泥于文档,我相信,就算公司里没有一份专门的文档说明以上的内容,但肯定也是在工作中提到过这些。
  3、用例设计
  网络上和教科书上有很多关于白盒用例设计的内容,包括逻辑覆盖法(语句、判定、条件、判定_条件、条件组合、路径)、根据控制流的基本路径法、还有数据流测试等,这里就不多说了。
  但是要补充一点的是,不管做什么测试,我还是认为功能测试要最先考虑。在保证了功能的基础上,再考虑白盒用例的设计。这并不矛盾,有的人可能会认为功能测试就是在程序的界面上操作,其实不然。
  每个方法一定都有自己的功能,先测试这个方法的功能,用黑盒用例设计方法设计测试用例,比如等价类、边界值、因果图等。等这些黑盒的用例验证过被测试的方法没有错误后,再考虑用白盒用例的设计方法进行用例设计。
  以测试界里一个很经典的三角形问题为例,我们来谈谈单元测试用例设计可以怎么设计。
  三角形问题:三个1-200之间的整数代表三角形的三条边a、b、c,判断这是一个等边三角形?还是等腰三角形?还是一般三角形?
  设计如下:c1、c2、c3三个方法是用来判断三条边的范围,isTriangle方法用来判断三边是不是能组成三角形,这四个方法都是private的访问权限,isType方法是用来判断三角形的类型,最后还有一个方法是setTriangle用来初始化三角形,这两个方法的访问权限是public。
  
图2 三角形类的设计
  根据设计,开发人员编写出以下的产品代码:
  【代码清单】
  public class Triangle
  {
  private int a;
  private int b;
  private int c;
  privateboolean c1(){   //判断a边的范围
  boolean flaga=true;
  if(a>=1&&a<=200){
  flaga=true;
  }
  else{
  flaga=false;
  }
  return flaga;
  }
  private boolean c2(){   //判断b边的范围
  boolean flagb=true;
  if(b>=1&&b<=200){
  flagb=true;
  }
  else{
  flagb=false;
  }
  return flagb;
  }
  private boolean c3(){   //判断c边的范围
  boolean flagc=true;
  if(c>=1&&c<=200){
  flagc=true;
  }
  else{
  flagc=false;
  }
  return flagc;
  }
  private boolean IsTriangle(){//根据三角形三边的性质,判断a、b、c是否能组成一个三角形
  boolean flagTriangle=true;
  if(c1()&c2()&c3()){
  if(a<b+c&b<a+c &c<a+b){
  flagTriangle=true;
  }
  else{
  flagTriangle=false;
  }
  }
  else{
  flagTriangle=false;
  }
  return flagTriangle;
  }
  public String IsType(){
  String str=" ";
  if(IsTriangle()){
  if(a==b&&a==c&&b==c){
  str="等边三角形";
  }
  else if(a==b||a==c||b==c){
  str="等腰三角形";
  }
  else{
  str="一般三角形";
  }
  }
  else{
  str="不能组成三角形";
  }
  return str;
  }
  }

小皮球的故事 发表于 2017-6-1 16:40:39

根据这段代码的功能,用判定表的方法:
  设计如下用例:
  
图3 三角形问题测试用例
  若图3中的用例实施后,没有发现什么问题,那么从某种程度上,我们可以相信被测试的代码是可以值得信赖的。这是,我们再针对一个一个的方法进行白盒用例设计。
  例如以下的代码段:
  private boolean IsTriangle(){//根据三角形三边的性质,判断a、b、c是否能组成一个三角形
  boolean flagTriangle=true;
  if(c1()&c2()&c3()){
  if(a<b+c&b<a+c &c<a+b)
  flagTriangle=true;
  }
  else{
  flagTriangle=false;
  }
  }
  else{
  flagTriangle=false;
  }
  return flagTriangle;
  }
  根据条件覆盖的思想进行用例设计:
  条件覆盖:
  判定1中:c1()&c2()&c3()
  c1:201 3
  c2:-34
  c3:0   5
  ①3,4,5
  ②201,-3,0
  判定2中:a<b+c & b<a+c & c<a+b
  a<b+c   b<a+cc<a+b
  ①3,4,5
  ②7,3,4
  ③8,3,4
  ④3,7,4
  ⑤3,8,4
  ⑥3,4,7
  ⑦3,4,8
  当然,你会发现白盒方式设计的用例,有些已经被黑盒方式的用例覆盖了,但不能说白盒用例设计就是没有价值的。这两种方式设计的用例,一综合起来,就能成了测试用例集合,这个集合中的用例,既能在功能上保证软件质量,也能从代码的结构上保证我们都测试到了。
  总的来说,不管是什么测试,功能测试还是优先级最高的。最先要搞清楚这一段代码的功能是什么,它要做什么,传什么参数进去,实施了一番动作后,返回什么出来;从这个角度去设计用例,你可以用总所周知的等价类、边界值、判定表、正交……这类用例设计方法。然后运行这些测试,确定功能没有大问题了,再辅助以白盒测试用例设计,比如逻辑覆盖分析法设计用例。
  三、        单元测试实施
  具体到了实施的部分,就和测试的平台、框架以及编写代码的语言相关了。大家的提问我看到了,很多都是关于单元测试的实施部分的。有问关于平台的单元测试,例如Android;也有问关于某种语言的单元测试,例如C、python;也有问关于单元测试框架的问题,例如Scala Test。
  我想针对这类问题先划分一下类别,不一定非常严谨:
  单元测试在各个平台上都有可能会需要开展:例如常见的Win/Linux、OS X、Phone/Mobile、Android、嵌入式
  要进行单元测试的应用使用不同的语言:C/C++、C#、Java、JavaScript、JSP、ASP、PHP……
  各种语言上的单元测试工具也很多,针对C/C++有大家耳熟能详的CUnit和CPPTest,针对C#的有NUnit、java的话有Junit、TestNG、针对Javascript很多了,有JSUnit、YUI Test、Venus、Jest、jsUnity……
  这么多种平台、语言、测试框架,还有一堆测试框架上的框架,比如Junit上的框架EasyMock、Zohhak框架(是一个参数化的 JUnit 测试框架),那就更加数不过来了。
  我想可能是"单元测试"这个话题有点大,有点类似"编程语言"这种话题一样,范围比较广。所以要是想谈哪个单元测试工具好,估计会跟论坛中的人讨论哪种编程语言最好,产生一样的效果。
  所以我想回答各位的是,实施单元测试,我们要考虑的问题有:
  · 将来要写的测试代码在那种平台上运行
  · 我所测试的代码是哪种语言,我对语言本身是否熟悉
  · 我使用的测试框架是否和被测试的应用是低耦合的
  · 我最擅长最熟悉的测试框架是哪个?
  · 被测试的应用和我写的测试代码,是如何协同合作,两者之间如何保持了一定的独立性,这里的意思是生产代码(被测试的代码)和消费代码(你写的测试代码)要分开。
  · 单元测试经常会要考虑解除代码之间的依赖关系,那我们就要考虑代码之间原本的依赖是哪些。比如有人问"单元测试的时候测试的模块有调用外部的函数,调用函数假设为f(a,b),a,b是指针的话,我怎么测这个函数呀。写桩函数时指针是随机分配的",其实这时候,就可以对被调用的函数写个mock。
  例如:
  "        我们需要对运行在容器中的Servlet组件进行单元测试,那得需要一个容器吧?
  "        我们要对数据库进行操作,那得需要准备好数据库吧?
  "        我们在嵌入式平台上进行测试,那测试的代码在被嵌入的平台上能否运行?若是没法直接在硬件平台上得到反映,那的看看是否有一种替换的方案(仿真)
  "        我们可能在代码中引用了另一个团队的代码,可是另一个团队的代码还未及时发来,但我们又不能耽搁测试
  若是要解除依赖,会用哪个框架呢?可有API的说明?
  确定被测试的应用必须写单元测试吗?例如和数据库之间的交互,若是数据准备起来并不麻烦,且数据可以反复使用,没有那种运行一次就要把数据重新生成一次的麻烦,那我们为何不能用"Jmeter测试JDBC请求"这种方式进行测试呢?
  不管你是哪个平台、哪种语言、哪个测试工具,我想上面的问题都是在进行单元测试之前要先回答的。望各位在考虑单元测试的时候,不要仅仅着眼在哪个语言,哪个工具。
  四、        自动化持续集成
  之前讲过的测试设计和实施,都是自己在单打独斗的进行测试要做的。但是单元测试为何总是在企业总不如系统测试那么普及和深入人心呢,原因有很多,但我想,诸多原因之一,可能也包括缺乏一个放大家共同进行单元测试的协作平台。
  单元测试要在团队中普及和实现,我认为有以下4个要素:
  · 配置管理工具:对被测试的代码进行版本管理,以便单元测试的时候,可以知道被测试的代码在哪里获取。比如SVN/Git。
  · 构建平台:对被测试的代码、测试代码要进行编译,运行测试,这些事我们用一个工具完成,不要手动的点,这样的工具,就成为构建平台。比如Ant和Maven。
  · 测试框架:最好不要用main作为测试的入口,比如使用JUnit。JUnit 的优点是整个测试过程无人值守,开发无须在线参与和判断最终结果是否正确,可以很容易地一次性运行多个测试,使得开发更加关注测试逻辑的编写,而不是增加构建维护时间。
  推荐阅读:http://junit.sourceforge.net/javadoc/
  · 反馈平台:把以上的三个要素组合起来,运行完测试,得到一个比较好理解的测试报告。比如Jenkins&Sonar。其中Jenkins能持续、自动地构建测试软件项目、监控一些定时执行的任务;而Sonar提供的测试报告是非常让人惊喜的,包括:单元测试覆盖率、成功率、代码注释、代码复杂度等度量数据。
  推荐阅读:https://jenkins.io/doc/
  五、        结束语
  从大家的问题中看,主要是关于单元测试的工具和如何开展。
  1、工具的选择,让大家产生了一种选择综合症的感觉,其实我看测试工具用什么,还是看被测试应用的平台、语言、环境。至于测试工具,个人感受的话,尽量用市面上见得多的,起码有啥问题找得到地方问。
  2、单元测试如何开展这个问题,我想:
  一来是单元测试的计划、方案、用例、执行这一类的问题;
  二来应该是各位很想了解某种平台某种语言下的某个测试框架要怎么用;
  三来是自动化持续构建怎么实施。
  对1、和2、的问题,由于篇幅和时间有限,我只能浅谈到此。
页: [1]
查看完整版本: 单元测试的实施