山西
400-650-7353
首页 > 计算机二级 > 【备考】Java 单元测试的 7 个技巧

【备考】Java 单元测试的 7 个技巧

2018-08-30 16:35:54 来源:互联网 荐:IT资料豪华升级版,点击加群领取

软件开发测试的类型

单元测试用于测试各个代码组件,并确保代码按照预期的方式工作。单元测试由开发人员编写和执行。大多数情况下,使用JUnit或TestNG之类的测试框架。测试用例通常是在方法级别写入并通过自动化执行。

集成测试检查系统是否作为一个整体而工作。集成测试也由开发人员完成,但不是测试单个组件,而是旨在跨组件测试。系统由许多单独的组件组成,如代码,数据库,Web服务器等。集成测试能够发现如组件布线,网络访问,数据库问题等问题。

功能测试通过将给定输入的结果与规范进行比较来检查每个功能是否正确实现。通常,这不是在开发人员级别的。功能测试由单独的测试团队执行。测试用例基于规范编写,并且实际结果与预期结果进行比较。有若干工具可用于自动化的功能测试,如Selenium和QTP。

如前所述,单元测试可帮助开发人员确定代码是否正常工作。在这篇博文中,我将提供在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时附加传递两个字符串:

  1. < class="hljs typescript">public String concat(boolean append, String a,String b) { 

  2. String result = null; 

  3. If (append) { 

  4. result = a + b; 

  5. return result.toLowerCase(); 

以下是上述方法的测试用例:

  1. <class="hljs less">@Test 

  2. public void testStringUtil() { 

  3. String result = stringUtil.concat(true, "Hello ", "World"); 

  4. System.out.println("Result is "+result); 

@Test

public void testStringUtil() {

String result = stringUtil.concat(true, "Hello ", "World");

System.out.println("Result is "+result);

}

在这种情况下,执行测试的值为true。当测试执行时,它将通过。当代码覆盖率工具运行时,它将显示100%的代码覆盖率,因为 concat 方法中的所有代码都被执行。但是,如果测试执行的值为false,则将抛出 NullPointerException 。所以100%的代码覆盖率并不真正表明测试覆盖了所有场景,也不能说明测试良好。

4.尽可能将测试数据外部化

在JUnit4之前,测试用例要运行的数据必须硬编码到测试用例中。这导致了限制,为了使用不同的数据运行测试,测试用例代码必须修改。但是,JUnit4以及TestNG支持外部化测试数据,以便可以针对不同的数据集运行测试用例,而无需更改源代码。

下面的 MathChecker 类有方法可以检查一个数字是否是奇数:

  1. < class="hljs kotlin">public class MathChecker { 

  2. public Boolean isOdd(int n) { 

  3. if (n%2 != 0) { 

  4. return true; 

  5. } else { 

  6. return false; 

以下是MathChecker类的TestNG测试用例:

  1. < class="hljs less">public class MathCheckerTest { 

  2. private MathChecker checker; 

  3. @BeforeMethod 

  4. public void beforeMethod() { 

  5. checker = new MathChecker(); 

  6. @Test 

  7. @Parameters("num") 

  8. public void isOdd(int num) {  

  9. System.out.println("Running test for "+num); 

  10. Boolean result = checker.isOdd(num); 

  11. Assert.assertEquals(result, new Boolean(true)); 

以下是testng.xml(用于TestNG的配置文件),它具有要为其执行测试的数据:

  1. < class="hljs xml"><?xml version="1.0" encoding="UTF-8"?> 

  2. <suite name="ParameterExampleSuite" parallel="false"> 

  3. <test name="MathCheckerTest"> 

  4. <classes> 

  5. <parameter name="num" value="3"></parameter> 

  6. <class name="com.stormpath.demo.MathCheckerTest"/> 

  7. </classes> 

  8. </test> 

  9. <test name="MathCheckerTest1"> 

  10. <classes> 

  11. <parameter name="num" value="7"></parameter> 

  12. <class name="com.stormpath.demo.MathCheckerTest"/> 

  13. </classes> 

  14. </test> 

  15. </suite> 

可以看出,在这种情况下,测试将执行两次,值3和7各一次。除了通过XML配置文件指定测试数据之外,还可以通过DataProvider注释在类中提供测试数据。

JUnit

与TestNG类似,测试数据也可以外部化用于JUnit。以下是与上述相同MathChecker类的JUnit测试用例:

  1. < class="hljs java">@RunWith(Parameterized.class) 

  2. public class MathCheckerTest { 

  3. private int inputNumber; 

  4. private Boolean expected; 

  5. private MathChecker mathChecker; 

  6. @Before 

  7. public void setup(){ 

  8. mathChecker = new MathChecker(); 

  9. // Inject via constructor 

  10. public MathCheckerTest(int inputNumber, Boolean expected) { 

  11. this.inputNumber = inputNumber; 

  12. this.expected = expected; 

  13. @Parameterized.Parameters 

  14. public static Collection<Object[]> getTestData() { 

  15. return Arrays.asList(new Object[][]{ 

  16. {1, true}, 

  17. {2, false}, 

  18. {3, true}, 

  19. {4, false}, 

  20. {5, true} 

  21. }); 

  22. @Test 

  23. public void testisOdd() { 

  24. System.out.println("Running test for:"+inputNumber); 

  25. assertEquals(mathChecker.isOdd(inputNumber), expected); 

可以看出,要对其执行测试的测试数据由getTestData()方法指定。此方法可以轻松地修改为从外部文件读取数据,而不是硬编码数据。

5.使用断言而不是Print语句

许多新手开发人员习惯于在每行代码之后编写System.out.println语句来验证代码是否正确执行。这种做法常常扩展到单元测试,从而导致测试代码变得杂乱。除了混乱,这需要开发人员手动干预去验证控制台上打印的输出,以检查测试是否成功运行。更好的方法是使用自动指示测试结果的断言。

下面的 StringUti 类是一个简单类,有一个连接两个输入字符串并返回结果的方法:

  1. < class="hljs typescript">public class StringUtil { 

  2. public String concat(String a,String b) { 

  3. return a + b; 

以下是上述方法的两个单元测试:

  1. < class="hljs less">@Test 

  2. public void testStringUtil_Bad() { 

  3. String result = stringUtil.concat("Hello ", "World"); 

  4. System.out.println("Result is "+result); 

  5. @Test 

  6. public void testStringUtil_Good() { 

  7. String result = stringUtil.concat("Hello ", "World"); 

  8. assertEquals("Hello World", result); 

testStringUtil\_Bad将始终传递,因为它没有断言。开发人员需要手动地在控制台验证测试的输出。如果方法返回错误的结果并且不需要开发人员干预,则testStringUtil\_Good将失败。

编辑推荐IT学习交流群:点击加群 571960479

加群备注:网站(限时赠送500G海量IT学习资料)

关注微信:山西优就业(ID:ujiuyesx)

回复:简历模板,即可领取

【各类行业的简历模板】

回复:安装包的名称,例:AI,即可领取

AI、DW、PS、AX、C4D等破解版软件

【小U将毕生绝学传授于你,打倒HR,轻松搞定CEO】

相关推荐:开学季好课1折购

【备考】计算机二级考试MSOffice技巧:设置页面对话框的技巧

更多分享交流,礼品领取,海量干货,请扫描下方内部资料领取二维码,一不小心就爱上IT,成为了大佬!

免责声明:本文来源于网络,由网友提供或网络搜集,仅供个人交流学习参考使用,不涉及商业盈利目的。如有版权问题,请联系本站管理员予以更改或删除。谢谢合作!

热门标签: Java 单元测试

猜你喜欢

推荐阅读