关于Java单元测试,你需要知道的一切

发表于:2018-1-08 11:00

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:Lam    来源:个人博客

  单元测试评估
  单元测试任务
  1.接口功能测试:用来保证接口功能的正确性。
  2.局部数据结构测试(不常用):用来保证接口中的数据结构是正确的
  比如变量有无初始值
  变量是否溢出
  3.边界条件测试
  变量没有赋值(即为NULL)
  变量是数值(或字符)
  主要边界:最小值,最大值,无穷大(对于DOUBLE等)
  溢出边界(期望异常或拒绝服务):最小值-1,最大值+1
  临近边界:最小值+1,最大值-1
  变量是字符串
  引用“字符变量”的边界
  空字符串
  对字符串长度应用“数值变量”的边界
  变量是集合
  空集合
  对集合的大小应用“数值变量”的边界
  调整次序:升序、降序
  变量有规律
  比如对于Math.sqrt,给出n^2-1,和n^2+1的边界
  4.所有独立执行通路测试:保证每一条代码,每个分支都经过测试
  代码覆盖率
  语句覆盖:保证每一个语句都执行到了
  判定覆盖(分支覆盖):保证每一个分支都执行到
  条件覆盖:保证每一个条件都覆盖到true和false(即if、while中的条件语句)
  路径覆盖:保证每一个路径都覆盖到
  相关软件
  Cobertura:语句覆盖
  Emma: Eclipse插件Eclemma
  5.各条错误处理通路测试:保证每一个异常都经过测试
  代码覆盖率
  在做单元测试时,代码覆盖率常常被拿来作为衡量测试好坏的指标,甚至,用代码覆盖率来考核测试任务完成情况,比如,代码覆盖率必须达到80%或 90%。于是乎,测试人员费尽心思设计案例覆盖代码。用代码覆盖率来衡量,有利也有有弊。
  代码覆盖率 = 代码的覆盖程度,一种度量方式。
  语句覆盖(StatementCoverage)
  又称行覆盖(LineCoverage),段覆盖(SegmentCoverage),基本块覆盖(BasicBlockCoverage),这是最常用也是最常见的一种覆盖方式,就是度量被测代码中每个可执行语句是否被执行到了。 需要注意的是,单独一行的花括号{} 也常常被统计进去。 语句覆盖常常被人指责为“最弱的覆盖”,它只管覆盖代码中的执行语句,却不考虑各种分支的组合等等。 假如你的上司只要求你达到语句覆盖,那么你可以省下很多功夫,但是,换来的确实测试效果的不明显,很难更多地发现代码中的问题。
  这里举一个不能再简单的例子,我们看下面的被测试代码:
  int foo(int a, int b)
  {
     return  a / b;
  }
  假如我们的测试人员编写如下测试案例:
  TeseCase: a = 10, b = 5
  以上代码当b=0时程序异常,但是语句覆盖率为100%
  判定覆盖(DecisionCoverage)
  又称分支覆盖(BranchCoverage),所有边界覆盖(All-EdgesCoverage),基本路径覆盖(BasicPathCoverage),判定路径覆盖(Decision-Decision-Path)。它度量程序中每一个判定的分支是否都被测试到了。 这句话是需要进一步理解的,应该非常容易和下面说到的条件覆盖混淆。因此我们直接介绍第三种覆盖方式,然后和判定覆盖一起来对比,就明白两者是怎么回事了。
  条件覆盖(ConditionCoverage)它度量判定中的每个子表达式结果true和false是否被测试到了。为了说明判定覆盖和条件覆盖的区别,我们来举一个例子,假如我们的被测代码如下:
  int foo(int a, int b)
  {
      if (a < 10 || b < 10) // 判定
      {
          return 0; // 分支一
      }
      else
      {
          return 1; // 分支二
      }
  }
  设计判定覆盖案例时,我们只需要考虑判定结果为true和false两种情况,因此,我们设计如下的案例就能达到判定覆盖率100%:
  TestCaes1: a = 5, b = 任意数字  覆盖了分支一
  TestCaes2: a = 15, b = 15          覆盖了分支二
  设计条件覆盖案例时,我们需要考虑判定中的每个条件表达式结果,为了覆盖率达到100%,我们设计了如下的案例:
  TestCase1: a = 5, b = 5       true,  true
  TestCase4: a = 15, b = 15   false, false
  通过上面的例子,我们应该很清楚了判定覆盖和条件覆盖的区别。需要特别注意的是:条件覆盖不是将判定中的每个条件表达式的结果进行排列组合,而是只要每个条件表达式的结果true和false测试到了就OK了。因此,我们可以这样推论:完全的条件覆盖并不能保证完全的判定覆盖。比如上面的例子,假如我设计的案例为:
  TestCase1: a = 5, b = 15  true,  false   分支一
  TestCase1: a = 15, b = 5  false, true    分支一
  我们看到,虽然我们完整的做到了条件覆盖,但是我们却没有做到完整的判定覆盖,我们只覆盖了分支一。
  路径覆盖(PathCoverage)
  又称断言覆盖(PredicateCoverage)。它度量了是否函数的每一个分支都被执行了,测试函数中所有可能的路径。 这句话也非常好理解,就是所有可能的分支都执行一遍,有多个分支嵌套时,需要对多个分支进行排列组合,可想而知,测试路径随着分支的数量指数级别增加。比如下面的测试代码中有两个判定分支:
  int foo(int a, int b)
  {
      int nReturn = 0;
      if (a < 10)
      {// 分支一
          nReturn += 1;
      }
      if (b < 10)
      {// 分支二
          nReturn += 10;
      }
      return nReturn;
  }
  对上面的代码,我们分别针对我们前三种覆盖方式来设计测试案例:
  语句覆盖
  TestCase a = 5, b = 5   nReturn = 11
  语句覆盖率100%
  判定覆盖
  TestCase1 a = 5,   b = 5     nReturn = 11
  TestCase2 a = 15, b = 15   nReturn = 0
  判定覆盖率100%
  条件覆盖
  TestCase1 a = 5,   b = 15   nReturn = 1
  TestCase2 a = 15, b = 5     nReturn = 10
  条件覆盖率100%
  我们看到,上面三种覆盖率结果看起来都很酷!都达到了100%!主管可能会非常的开心,但是,让我们再去仔细的看看,上面被测代码中,nReturn的结果一共有四种可能的返回值:0,1,10,11,而我们上面的针对每种覆盖率设计的测试案例只覆盖了部分返回值,因此,可以说使用上面任一覆盖方式,虽然覆盖率达到了100%,但是并没有测试完全。接下来我们来看看针对路径覆盖设计出来的测试案例:
  TestCase1 a = 5,    b = 5     nReturn = 0
  TestCase2 a = 15,  b = 5     nReturn = 1
  TestCase3 a = 5,    b = 15   nReturn = 10
  TestCase4 a = 15,  b = 15   nReturn = 11
  路径覆盖率100%
  太棒了!路径覆盖将所有可能的返回值都测试到了。这也正是它被很多人认为是“最强的覆盖”的原因了。
  还有一些其他的覆盖方式,如:循环覆盖(LoopCoverage),它度量是否对循环体执行了零次,一次和多余一次循环。剩下一些其他覆盖方式就不介绍了。
  Jacoco
  Jacoco可以嵌入到Ant、Maven中,也可以使用Java Agent技术监控任意Java程序,也可以使用Java Api来定制功能。
  Jacoco会监控JVM中的调用,生成监控结果(默认保存在jacoco.exec文件中),然后分析此结果,配合源代码生成覆盖率报告。需要注意的是:监控和分析这两步,必须使用相同的Class文件,否则由于Class不同,而无法定位到具体的方法,导致覆盖率均为0%。
  Java Agent嵌入
  首先,需要下载jacocoagent.jar文件,然后在Java程序启动参数后面加上 -javaagent:[yourpath/]jacocoagent.jar=[option1]=[value1],[option2]=[value2],具体的options可以在此页面找到。默认会在JVM关闭时(注意不能是kill -9),输出监控结果到jacoco.exec文件中,也可以通过socket来实时地输出监控报告(可以在Example代码中找到简单实现)。
  Java Report
  可以使用Ant、Mvn或Eclipse来分析jacoco.exec文件,也可以通过API来分析。
  public void createReport() throws Exception {
      // 读取监控结果
      final FileInputStream fis = new FileInputStream(new File(“jacoco.exec”));
      final ExecutionDataReader executionDataReader = new ExecutionDataReader(fis);
      // 执行数据信息
      ExecutionDataStore executionDataStore = new ExecutionDataStore();
      // 会话信息
      SessionInfoStore sessionInfoStore = new SessionInfoStore();
      executionDataReader.setExecutionDataVisitor(executionDataStore);
      executionDataReader.setSessionInfoVisitor(sessionInfoStore);
      while (executionDataReader.read()) {}
      fis.close();
      // 分析结构
      final CoverageBuilder coverageBuilder = new CoverageBuilder();
      final Analyzer analyzer = new Analyzer(executionDataStore, coverageBuilder);
      // 传入监控时的Class文件目录,注意必须与监控时的一样
      File classesDirectory = new File(“classes”);
      analyzer.analyzeAll(classesDirectory);
      IBundleCoverage bundleCoverage = coverageBuilder.getBundle(“Title”);
      // 输出报告
      File reportDirectory = new File(“report”); // 报告所在的目录
      final HTMLFormatter htmlFormatter = new HTMLFormatter();  // HTML格式
      final IReportVisitor visitor = htmlFormatter.createVisitor(new FileMultiReportOutput(reportDirectory));
      // 必须先调用visitInfo
      visitor.visitInfo(sessionInfoStore.getInfos(), executionDataStore.getContents());
      File sourceDirectory = new File(“src”); // 源代码目录
      // 遍历所有的源代码
      // 如果不执行此过程,则在报告中只能看到方法名,但是无法查看具体的覆盖(因为没有源代码页面)
      visitor.visitBundle(bundleCoverage, new DirectorySourceFileLocator(sourceDirectory, “utf-8”, 4));
      // 执行完毕
      visitor.visitEnd();
  }
  Android testDebug任务找不到的原因http://stackoverflow.com/questions/31937815/task-with-name-testdebug-not-found-in-project-module
  其他(Android)
  test与androidTest的区别
  1.test为单元测试,androidTest为与界面有关的测试,包括自动化测试
  2.gradle项目依赖,testCompile与androidTestCompile
  3.androidTest需要在模拟器或者真机上运行
  http://stackoverflow.com/questions/29021331/confused-about-testcompile-and-androidtestcompile-in-android-gradle
  Studio单元测试步骤
  创建项目——根据需要testCompile或者androidTestCompile相应的测试支持库——设置Build Variant为相应的测试环境——Edit Configuration ——测试驱动开发,可以先写空实现的接口,通过IDE的快捷操作方式自动生成相应目录下的测试类——运行相应的TestCase。
  常见错误
  1.No tests found in …
  如果使用安卓自身的Instrumentation相关的测试,studio自动生成的TestCase不会去继承TestCase,是不会被Runner所判断为测试用例的,所以自然找不到测试,根据需要自己选择要继承的基类。
  单元测试不需要使用Instrumentation,
  This is where you need to select “Build Variants” on the left of Android Studio and make sure “Unit Tests” is selected
  一定要选择这个选项,否则所有的步骤都是以Instrumentation框架进行,大多用于自动化测试。
   2.

上文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理。
22/2<12
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号