编写更好的Java单元测试的七个技巧_第1页
编写更好的Java单元测试的七个技巧_第2页
编写更好的Java单元测试的七个技巧_第3页
编写更好的Java单元测试的七个技巧_第4页
编写更好的Java单元测试的七个技巧_第5页
已阅读5页,还剩5页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、编写更好的Java单元测试的七个技巧测试是开发的一个非常重要的方面,可以在很大程度上决定一个应用程序的命运。良好的测试可以在早期捕获导致应用程序崩溃的问题,但较差的测试往往总是导致故障和停机。作者:小峰来源:码农网|2016-12-13 10:06 收藏   分享   测试是开发的一个非常重要的方面,可以在很大程度上决定一个应用程序的命运。良好的测试可以在早期捕获导致应用程序崩溃的问题,但较差的测试往往总是导致故障和停机。虽然有三种主要类型的软件测试:单元测试,功能测试和集成测试,但是在这篇博文中,我们将讨论开发人员级单元测试。在我深入讲述具体

2、细节之前,让我们先来回顾一下这三种测试的详细内容。软件开发测试的类型单元测试用于测试各个代码组件,并确保代码按照预期的方式工作。单元测试由开发人员编写和执行。大多数情况下,使用JUnit或TestNG之类的测试框架。测试用例通常是在方法级别写入并通过自动化执行。集成测试检查系统是否作为一个整体而工作。集成测试也由开发人员完成,但不是测试单个组件,而是旨在跨组件测试。系统由许多单独的组件组成,如代码,数据库,Web服务器等。集成测试能够发现如组件布线,网络访问,数据库问题等问题。功能测试通过将给定输入的结果与规范进行比较来检查每个功能是否正确实现。通常,这不是在开发人员级别的。功能测试由单独的测

3、试团队执行。测试用例基于规范编写,并且实际结果与预期结果进行比较。有若干工具可用于自动化的功能测试,如Selenium和QTP。如前所述,单元测试可帮助开发人员确定代码是否正常工作。在这篇博文中,我将提供在Java中单元测试的有用提示。1.使用框架来用于单元测试Java提供了若干用于单元测试的框架。TestNG和JUnit是最流行的测试框架。JUnit和TestNG的一些重要功能:· 易于设置和运行。· 支持注释。· 允许忽略或分组并一起执行某些测试。· 支持参数化测试,即通过在运行时指定不同的值来运行单元测试。· 通过与构建工具,如Ant,M

4、aven和Gradle集成来支持自动化的测试执行。EasyMock是一个模拟框架,是单元测试框架,如JUnit和TestNG的补充。EasyMock本身不是一个完整的框架。它只是添加了创建模拟对象以便于测试的能力。例如,我们想要测试的一个方法可以调用从数据库获取数据的DAO类。在这种情况下,EasyMock可用于创建返回硬编码数据的MockDAO。这使我们能够轻松地测试我们意向的方法,而不必担心数据库访问。2.谨慎使用测试驱动开发!测试驱动开发(TDD)是一个软件开发过程,在这过程中,在开始任何编码之前,我们基于需求来编写测试。由于还没有编码,测试最初会失败。然后写入最小量的代码以通过测试。然

5、后重构代码,直到被优化。目标是编写覆盖所有需求的测试,而不是一开始就写代码,却可能甚至都不能满足需求。TDD是伟大的,因为它导致简单的模块化代码,且易于维护。总体开发速度加快,容易发现缺陷。此外,单元测试被创建作为TDD方法的副产品。然而,TDD可能不适合所有的情况。在设计复杂的项目中,专注于最简单的设计以便于通过测试用例,而不提前思考可能会导致巨大的代码更改。此外,TDD方法难以用于与遗留系统,GUI应用程序或与数据库一起工作的应用程序交互的系统。另外,测试需要随着代码的改变而更新。因此,在决定采用TDD方法之前,应考虑上述因素,并应根据项目的性质采取措施。3.测量代码覆盖率代码覆盖率衡量(

6、以百分比表示)了在运行单元测试时执行的代码量。通常,高覆盖率的代码包含未检测到的错误的几率要低,因为其更多的源代码在测试过程中被执行。测量代码覆盖率的一些最佳做法包括:· 使用代码覆盖工具,如Clover,Corbetura,JaCoCo或Sonar。使用工具可以提高测试质量,因为这些工具可以指出未经测试的代码区域,让你能够开发开发额外的测试来覆盖这些领域。· 每当写入新功能时,立即写新的测试覆盖。· 确保有测试用例覆盖代码的所有分支,即if / else语句。高代码覆盖不能保证测试是完美的,所以要小心!下面的concat方法接受布尔值作为输入,并且仅当布尔值为t

7、rue时附加传递两个字符串:1. public String concat(boolean append, String a,String b)  2.         String result = null; 3.         If (append)  4.   

8、;          result = a + b; 5.                              6.    

9、     return result.toLowerCase(); 7.  以下是上述方法的测试用例: 1. Test 2. public void testStringUtil()  3.      String result = stringUtil.concat(true, "Hello ", "World");

10、 System.out.println("Result is "+result); 4.  在这种情况下,执行测试的值为true。当测试执行时,它将通过。当代码覆盖率工具运行时,它将显示100%的代码覆盖率,因为concat方法中的所有代码都被执行。但是,如果测试执行的值为false,则将抛出NullPointerException。所以100%的代码覆盖率并不真正表明测试覆盖了所有场景,也不能说明测试良好。4.尽可能将测试数据外部化在JUnit4之前,测试用例要运行的数据必须硬编码到测试用例中。这导致了限制,为了使用不同的

11、数据运行测试,测试用例代码必须修改。但是,JUnit4以及TestNG支持外部化测试数据,以便可以针对不同的数据集运行测试用例,而无需更改源代码。下面的MathChecker类有方法可以检查一个数字是否是奇数: 1. public class MathChecker  2.         public Boolean isOdd(int n)  3.       &

12、#160;     if (n%2 != 0)  4.                 return true; 5.              else  6. 

13、60;               return false; 7.              8.          9.      以下是MathCheck

14、er类的TestNG测试用例: 1. public class MathCheckerTest  2.         private MathChecker checker; 3.         BeforeMethod 4.         public void

15、 beforeMethod()  5.           checker = new MathChecker(); 6.          7.         Test 8.       

16、;  Parameters("num") public void isOdd(int num)  System.out.println("Running test for "+num); Boolean result = checker.isOdd(num); Assert.assertEquals(result, new Boolean(true); 9. 

17、0;        10.      TestNG以下是testng.xml(用于TestNG的配置文件),它具有要为其执行测试的数据:1. <xml version="1.0" encoding="UTF-8"?> 2. <suite name="ParameterExampleSuite" parallel="false&qu

18、ot;> 3. <test name="MathCheckerTest"> 4. <classes> 5.   <parameter name="num" value="3"></parameter> 6.   <class name="com.stormpath.demo.MathCheckerTest"/> 7. &l

19、t;/classes> 8.  </test> 9.  <test name="MathCheckerTest1"> 10. <classes> 11.   <parameter name="num" value="7"></parameter> 12.   <class name="com.stormpa

20、th.demo.MathCheckerTest"/> 13. </classes> 14.  </test> 15. </suite> 可以看出,在这种情况下,测试将执行两次,值3和7各一次。除了通过XML配置文件指定测试数据之外,还可以通过DataProvider注释在类中提供测试数据。JUnit与TestNG类似,测试数据也可以外部化用于JUnit。以下是与上述相同MathChecker类的JUnit测试用例:1. RunWith(Parameterized.class) 2.

21、public class MathCheckerTest  3.  private int inputNumber; 4.  private Boolean expected; 5.  private MathChecker mathChecker; 6.  Before 7.  public void setup() 8.     &#

22、160;mathChecker = new MathChecker(); 9.   10.     / Inject via constructor 11.     public MathCheckerTest(int inputNumber, Boolean expected)  12.      

23、0;  this.inputNumber = inputNumber; 13.         this.expected = expected; 14.      15.     Parameterized.Parameters 16.     public static Co

24、llection<Object> getTestData()  17.         return Arrays.asList(new Object 18.                 1, true, 19.     &

25、#160;           2, false, 20.                 3, true, 21.               &#

26、160; 4, false, 22.                 5, true 23.         ); 24.      25.     Test 26.   

27、0; public void testisOdd()  27.         System.out.println("Running test for:"+inputNumber); 28.         assertEquals(mathChecker.isOdd(inputNumber), expected); 

28、;29.      30.  可以看出,要对其执行测试的测试数据由getTestData()方法指定。此方法可以轻松地修改为从外部文件读取数据,而不是硬编码数据。5.使用断言而不是Print语句许多新手开发人员习惯于在每行代码之后编写System.out.println语句来验证代码是否正确执行。这种做法常常扩展到单元测试,从而导致测试代码变得杂乱。除了混乱,这需要开发人员手动干预去验证控制台上打印的输出,以检查测试是否成功运行。更好的方法是使用自动指示测试结果的断言。下面的StringUti类是一个简单类,有一个连接两个输入字符串并

29、返回结果的方法:1. public class StringUtil  2.         public String concat(String a,String b)  3.             return a + b; 4.  

30、0;       5.      以下是上述方法的两个单元测试:1. Test 2.     public void testStringUtil_Bad()  3.          String result = stringUtil.concat("He

31、llo ", "World"); 4.          System.out.println("Result is "+result); 5.      6.     Test 7.     public void testStringU

32、til_Good()  8.          String result = stringUtil.concat("Hello ", "World"); 9.          assertEquals("Hello World", result);

33、0;10.      testStringUtil_Bad将始终传递,因为它没有断言。开发人员需要手动地在控制台验证测试的输出。如果方法返回错误的结果并且不需要开发人员干预,则testStringUtil_Good将失败。6.构建具有确定性结果的测试一些方法不具有确定性结果,即该方法的输出不是预先知道的,并且每一次都可以改变。例如,考虑以下代码,它有一个复杂的函数和一个计算执行复杂函数所需时间(以毫秒为单位)的方法:1. public class DemoLogic  2.  private&#

34、160;void veryComplexFunction() 3.      /This is a complex function that has a lot of database access and is time consuming 4.      /To demo this 

35、method, I am going to add a Thread.sleep for a random number of milliseconds 5.      try  6.          int time = (int) (Math.rando

36、m()*100); 7.          Thread.sleep(time); 8.       catch (InterruptedException e)  9.          / TODO Auto-generated catch block 10.          e.pr

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论