编写 Java 单元测试的实用技巧

发表于:2018-1-25 10:10

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

 作者:ApplySquare    来源:简书

#
java
分享:
  就个人的经历来说总结了一下七个方面的内容,希望可以对题主有所帮助吧
  1.使用框架来用于单元测试
  Java提供了若干用于单元测试的框架。TestNG和JUnit是最流行的测试框架。JUnit和TestNG的一些重要功能:
  易于设置和运行。
  支持注释。
  允许忽略或分组并一起执行某些测试。
  支持参数化测试,即通过在运行时指定不同的值来运行单元测试。
  通过与构建工具,如Ant,Maven和Gradle集成来支持自动化的测试执行。
  EasyMock是一个模拟框架,是单元测试框架,如JUnit和TestNG的补充。EasyMock本身不是一个完整的框架。它只是添加了创建模拟对象以便于测试的能力。例如,我们想要测试的一个方法可以调用从数据库获取数据的DAO类。在这种情况下,EasyMock可用于创建返回硬编码数据的MockDAO。这使我们能够轻松地测试我们意向的方法,而不必担心数据库访问。
  2.谨慎使用测试驱动开发!
  测试驱动开发(TDD)是一个软件开发过程,在这过程中,在开始任何编码之前,我们基于需求来编写测试。由于还没有编码,测试最初会失败。然后写入最小量的代码以通过测试。然后重构代码,直到被优化。
  目标是编写覆盖所有需求的测试,而不是一开始就写代码,却可能甚至都不能满足需求。TDD是伟大的,因为它导致简单的模块化代码,且易于维护。总体开发速度加快,容易发现缺陷。此外,单元测试被创建作为TDD方法的副产品。
  然而,TDD可能不适合所有的情况。在设计复杂的项目中,专注于最简单的设计以便于通过测试用例,而不提前思考可能会导致巨大的代码更改。此外,TDD方法难以用于与遗留系统,GUI应用程序或与数据库一起工作的应用程序交互的系统。另外,测试需要随着代码的改变而更新。
  因此,在决定采用TDD方法之前,应考虑上述因素,并应根据项目的性质采取措施。
  3.测量代码覆盖率
  代码覆盖率衡量(以百分比表示)了在运行单元测试时执行的代码量。通常,高覆盖率的代码包含未检测到的错误的几率要低,因为其更多的源代码在测试过程中被执行。测量代码覆盖率的一些最佳做法包括:
  使用代码覆盖工具,如Clover,Corbetura,JaCoCo或Sonar。使用工具可以提高测试质量,因为这些工具可以指出未经测试的代码区域,让你能够开发开发额外的测试来覆盖这些领域。
  每当写入新功能时,立即写新的测试覆盖。
  确保有测试用例覆盖代码的所有分支,即if / else语句。
  高代码覆盖不能保证测试是完美的,所以要小心!
  下面的concat方法接受布尔值作为输入,并且仅当布尔值为true时附加传递两个字符串:
  以下是上述方法的测试用例:
  在这种情况下,执行测试的值为true。当测试执行时,它将通过。当代码覆盖率工具运行时,它将显示100%的代码覆盖率,因为concat方法中的所有代码都被执行。但是,如果测试执行的值为false,则将抛出NullPointerException。所以100%的代码覆盖率并不真正表明测试覆盖了所有场景,也不能说明测试良好。
  4.尽可能将测试数据外部化
  在JUnit4之前,很多计算机科学专业的同学-测试用例要运行的数据必须硬编码到测试用例中。这导致了限制,为了使用不同的数据运行测试,测试用例代码必须修改。但是,JUnit4以及TestNG支持外部化测试数据,以便可以针对不同的数据集运行测试用例,而无需更改源代码。
  下面的MathChecker类有方法可以检查一个数字是否是奇数:
  以下是MathChecker类的TestNG测试用例:
  TestNG
  以下是testng.xml(用于TestNG的配置文件),它具有要为其执行测试的数据:
  可以看出,在这种情况下,测试将执行两次,值3和7各一次。除了通过XML配置文件指定测试数据之外,还可以通过DataProvider注释在类中提供测试数据。
  JUnit
  与TestNG类似,测试数据也可以外部化用于JUnit。以下是与上述相同MathChecker类的JUnit测试用例:
  可以看出,要对其执行测试的测试数据由getTestData()方法指定。此方法可以轻松地修改为从外部文件读取数据,而不是硬编码数据。
  5.使用断言而不是Print语句
  许多新手开发人员习惯于在每行代码之后编写System.out.println语句来验证代码是否正确执行。这种做法常常扩展到单元测试,从而导致测试代码变得杂乱。除了混乱,这需要开发人员手动干预去验证控制台上打印的输出,以检查测试是否成功运行。更好的方法是使用自动指示测试结果的断言。
  下面的StringUti类是一个简单类,有一个连接两个输入字符串并返回结果的方法:
  以下是上述方法的两个单元测试:
  testStringUtil\_Bad将始终传递,因为它没有断言。开发人员需要手动地在控制台验证测试的输出。如果方法返回错误的结果并且不需要开发人员干预,则testStringUtil\_Good将失败。
  6.构建具有确定性结果的测试
  一些方法不具有确定性结果,即该方法的输出不是预先知道的,并且每一次都可以改变。例如,考虑以下代码,它有一个复杂的函数和一个计算执行复杂函数所需时间(以毫秒为单位)的方法:
  publicclassDemoLogic{
      privatevoidveryComplexFunction(){
          //This is a complex function that has a lot of database access and is time consuming//To demo this method, I am going to add a Thread.sleep for a random number of millisecondstry {
              int time = (int) (Math.random()*100);
              Thread.sleep(time);
          } catch (InterruptedException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
          }
      }
      publiclongcalculateTime(){
          long time = 0;
          long before = System.currentTimeMillis();
          veryComplexFunction();
          long after = System.currentTimeMillis();
          time = after - before;
          return time;
      }
      }
  在这种情况下,每次执行calculateTime方法时,它将返回一个不同的值。为该方法编写测试用例不会有任何用处,因为该方法的输出是可变的。因此,测试方法将不能验证任何特定执行的输出。
  7.除了正面情景外,还要测试负面情景和边缘情况
  通常,开发人员会花费大量的时间和精力编写测试用例,以确保应用程序按预期工作。然而,测试负面测试用例也很重要。负面测试用例指的是测试系统是否可以处理无效数据的测试用例。例如,考虑一个简单的函数,它能读取长度为8的字母数字值,由用户键入。除了字母数字值,应测试以下负面测试用例:
  用户指定非字母数字值,如特殊字符。
  用户指定空值。
  用户指定大于或小于8个字符的值。
  类似地,边界测试用例测试系统是否适用于极端值。例如,如果用户希望输入从1到100的数字值,则1和100是边界值,对这些值进行测试系统是非常重要的。


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

关注51Testing

联系我们

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

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

沪ICP备05003035号

沪公网安备 31010102002173号