Java4Android之单元测试入门

本文主要介绍了如何使用 JUnit 4 提供的各种功能开展有效的单元测试,并通过一个实例演示了如何使用 Ant 执行自动化的单元测试。本文假设读者对 Eclipse 下进行 Java
开发有一定的经验,并了解 Java 5 中的注解(annotation)特性。 
引言 

    毋庸置疑,程序员要对自己编写的代码负责,您不仅要保证它能通过编译,正常地运行,而且要满足需求和设计预期的效果。单元测试正是验证代码行为是否满足预期的有效手段之一。但不可否认,做测试是件很枯燥无趣的事情,而一遍又一遍的测试则更是让人生畏的工作。幸运的是,单元测试工具
JUnit 使这一切变得简单艺术起来。 

    JUnit 是 Java 社区中知名度最高的单元测试工具。它诞生于 1997 年,由 Erich Gamma 和 Kent Beck 共同开发完成。其中
Erich Gamma 是经典著作《设计模式:可复用面向对象软件的基础》一书的作者之一,并在 Eclipse 中有很大的贡献;Kent Beck 则是一位极限编程(XP)方面的专家和先驱。 

    麻雀虽小,五脏俱全。JUnit 设计的非常小巧,但是功能却非常强大。Martin Fowler 如此评价 JUnit:在软件开发领域,从来就没有如此少的代码起到了如此重要的作用。它大大简化了开发人员执行单元测试的难度,特别是
JUnit 4 使用 Java 5 中的注解(annotation)使测试变得更加简单。 

JUnit 4 初体验 

    在开始体验 JUnit 4 之前,我们需要以下软件的支持: 

    Eclipse:最为流行的 IDE,它全面集成了 JUnit,并从版本 3.2 开始支持 JUnit 4。当然 JUnit 并不依赖于任何
IDE。您可以从 http://www.eclipse.org/ 上下载最新的 Eclipse 版本。 
    Ant:基于 Java 的开源构建工具,您可以在 http://ant.apache.org/ 上得到最新的版本和丰富的文档。Eclipse
中已经集成了 Ant,但是在撰写本文时,Eclipse 使用的 Ant 版本较低(必需 1.7 或者以上版本),不能很好的支持 JUnit 4。 
    JUnit:它的官方网站是 http://www.junit.org/。您可以从上面获取关于 JUnit 的最新消息。如果您和本文一样在 Eclipse
中使用 JUnit,就不必再下载了。 
    首先为我们的体验新建一个 Java 工程 —— coolJUnit。现在需要做的是,打开项目 coolJUnit 的属性页 -> 选择“Java
Build Path”子选项 -> 点选“Add Library…”按钮 -> 在弹出的“Add Library”对话框中选择 JUnit(图1),并在下一页中选择版本 4.1 后点击“Finish”按钮。这样便把 JUnit 引入到当前项目库中了。 

图1 为项目添加 JUnit 库 
  

请注意 JDK 的版本 

    JUnit 4.1 是基于 Java 5 的升级版本,它使用了 Tiger 中的很多新特性来简化原有的使用方式。正因为如此,它并不能直接运行在
JDK1.4.x 版本上。如果您需要在 JDK1.4.x 版本使用 JUnit 的话,请使用 3.8.1 版本。 

    可以开始编写单元测试了吗?等等……,您打算把单元测试代码放在什么地方呢?把它和被测试代码混在一起,这显然会照成混乱,因为单元测试代码是不会出现在最终产品中的。建议您分别为单元测试代码与被测试代码创建单独的目录,并保证测试代码和被测试代码使用相同的包名。这样既保证了代码的分离,同时还保证了查找的方便。遵照这条原则,我们在项目 coolJUnit
根目录下添加一个新目录 testsrc,并把它加入到项目源代码目录中(加入方式见 图2)。 

图2 修改项目源代码目录 
  

    现在我们得到了一条 JUnit 的最佳实践:单元测试代码和被测试代码使用一样的包,不同的目录。 

    一切准备就绪,一起开始体验如何使用 JUnit 进行单元测试吧。下面的例子来自笔者的开发实践:工具类 WordDealUtil 中的静态方法
wordFormat4DB 是专用于处理 Java 对象名称向数据库表名转换的方法(您可以在代码注释中可以得到更多详细的内容)。下面是第一次编码完成后大致情形: 

Java代码  

  1. package com.ai92.cooljunit;  
  2.   
  3. import java.util.regex.Matcher;  
  4. import java.util.regex.Pattern;  
  5.   
  6.   
  7. public class WordDealUtil {  
  8.   
  9.       
  10.     public static String wordFormat4DB(String name){  
  11.         Pattern p = Pattern.compile("[A-Z]");  
  12.         Matcher m = p.matcher(name);  
  13.         StringBuffer sb = new StringBuffer();  
  14.           
  15.         while(m.find()){  
  16.             m.appendReplacement(sb, "_"+m.group());  
  17.         }  
  18.         return m.appendTail(sb).toString().toLowerCase();  
  19.     }  
  20. }  

    它是否能按照预期的效果执行呢?尝试为它编写 JUnit 单元测试代码如下: 

Java代码  

  1. package com.ai92.cooljunit;  
  2.   
  3. import static org.junit.Assert.assertEquals;  
  4. import org.junit.Test;  
  5.   
  6. public class TestWordDealUtil {  
  7.     //测试wordFormat4DB正常运行的情况  
  8.     @Test public void wordFormat4DBNormal(){  
  9.         String target = "employeeInfo";  
  10.         String result = WordDealUtil.wordFormat4DB(target);  
  11.           
  12.         assertEquals("employee_info", result);  
  13.     }  
  14. }  

    很普通的一个类嘛!测试类 TestWordDealUtil 之所以使用“Test”开头,完全是为了更好的区分测试类与被测试类。测试方法 wordFormat4DBNormal
调用执行被测试方法 WordDealUtil.wordFormat4DB,以判断运行结果是否达到设计预期的效果。需要注意的是,测试方法 wordFormat4DBNormal 需要按照一定的规范书写: 

    测试方法必须使用注解 org.junit.Test 修饰。 
测试方法必须使用 public void 修饰,而且不能带有任何参数。 
    测试方法中要处理的字符串为“employeeInfo”,按照设计目的,处理后的结果应该为“employee_info”。assertEquals
是由 JUnit 提供的一系列判断测试结果是否正确的静态断言方法(位于类 org.junit.Assert 中)之一,我们使用它将执行结果 result 和预期值“employee_info”进行比较,来判断测试是否成功。 

    看看运行结果如何。在测试类上点击右键,在弹出菜单中选择 Run As JUnit Test。运行结果如下图所示: 

图3 JUnit 运行成功界面 
  

    绿色的进度条提示我们,测试运行通过了。但现在就宣布代码通过了单元测试还为时过早。记住:您的单元测试代码不是用来证明您是对的,而是为了证明您没有错。因此单元测试的范围要全面,比如对边界值、正常值、错误值得测试;对代码可能出现的问题要全面预测,而这也正是需求分析、详细设计环节中要考虑的。显然,我们的测试才刚刚开始,继续补充一些对特殊情况的测试: 

Java代码

  1. public class TestWordDealUtil {  
  2.     ……  
  3.     //测试 null 时的处理情况  
  4.     @Test public void wordFormat4DBNull(){  
  5.         String target = null;  
  6.         String result = WordDealUtil.wordFormat4DB(target);  
  7.           
  8.         assertNull(result);  
  9.     }  
  10.       
  11.     //测试空字符串的处理情况  
  12.     @Test public void wordFormat4DBEmpty(){  
  13.         String target = "";  
  14.         String result = WordDealUtil.wordFormat4DB(target);  
  15.           
  16.         assertEquals("", result);  
  17.     }  
  18.   
  19.     //测试当首字母大写时的情况  
  20.     @Test public void wordFormat4DBegin(){  
  21.         String target = "EmployeeInfo";  
  22.         String result = WordDealUtil.wordFormat4DB(target);  
  23.           
  24.         assertEquals("employee_info", result);  
  25.     }  
  26.       
  27.     //测试当尾字母为大写时的情况  
  28.     @Test public void wordFormat4DBEnd(){  
  29.         String target = "employeeInfoA";  
  30.         String result = WordDealUtil.wordFormat4DB(target);  
  31.           
  32.         assertEquals("employee_info_a", result);  
  33.     }  
  34.       
  35.     //测试多个相连字母大写时的情况  
  36.     @Test public void wordFormat4DBTogether(){  
  37.         String target = "employeeAInfo";  
  38.         String result = WordDealUtil.wordFormat4DB(target);  
  39.           
  40.         assertEquals("employee_a_info", result);  
  41.     }  
  42. }  

    再次运行测试。很遗憾,JUnit 运行界面提示我们有两个测试情况未通过测试(图4)——当首字母大写时得到的处理结果与预期的有偏差,造成测试失败(failure);而当测试对 null 的处理结果时,则直接抛出了异常——测试错误(error)。显然,被测试代码中并没有对首字母大写和
null 这两种特殊情况进行处理,修改如下: 

Java代码  

  1. //修改后的方法wordFormat4DB  
  2.   
  3.     public static String wordFormat4DB(String name){  
  4.           
  5.         if(name == null){  
  6.             return null;  
  7.         }  
  8.           
  9.         Pattern p = Pattern.compile("[A-Z]");  
  10.         Matcher m = p.matcher(name);  
  11.         StringBuffer sb = new StringBuffer();  
  12.           
  13.         while(m.find()){  
  14.             if(m.start() != 0)  
  15.                 m.appendReplacement(sb, ("_"+m.group()).toLowerCase());  
  16.         }  
  17.         return m.appendTail(sb).toString().toLowerCase();  
  18.     }  

图4 JUnit 运行失败界面 
  

    JUnit 将测试失败的情况分为两种:failure 和 error。Failure 一般由单元测试使用的断言方法判断失败引起,它表示在测试点发现了问题;而 error 则是由代码异常引起,这是测试目的之外的发现,它可能产生于测试代码本身的错误(测试代码也是代码,同样无法保证完全没有缺陷),也可能是被测试代码中的一个隐藏的bug。 

请牢记! 

    请牢记这一条 JUnit 最佳实践:测试任何可能的错误。单元测试不是用来证明您是对的,而是为了证明您没有错。 
  
    啊哈,再次运行测试,绿条又重现眼前。通过对 WordDealUtil.wordFormat4DB 比较全面的单元测试,现在的代码已经比较稳定,可以作为 API 的一部分提供给其它模块使用了。 

    不知不觉中我们已经使用 JUnit 漂亮的完成了一次单元测试。可以体会到 JUnit 是多么轻量级,多么简单,根本不需要花心思去研究,这就可以把更多的注意力放在更有意义的事情上——编写完整全面的单元测试

时间: 2024-08-01 12:30:32

Java4Android之单元测试入门的相关文章

JUnit和单元测试入门简介

JUnit和单元测试入门简介 1.几个相关的概念 白盒测试--把测试对象看作一个打开的盒子,程序内部的逻辑结构和其他信息对测试人员是公开的. 回归测试--软件或环境的修复或更正后的"再测试",自动测试工具对这类测试尤其有用. 单元测试--是最小粒度的测试,以测试某个功能或代码块.一般由程序员来做,因为它需要知道内部程序设计和编码的细节. JUnit --是一个开发源代码的Java测试框架,用于编写和运行可重复的测试.他是用于单元测试框架体系xUnit的一个实例(用于java语言).主要

Java4Android之HttpClient入门使用代码集

本文将从代码的角度去引导如何使用httpclient的各个功能和特性. 第一个程序 import org.apache.commons.httpclient.*; import org.apache.commons.httpclient.methods.*; import org.apache.commons.httpclient.params.HttpMethodParams; import java.io.*; public class HttpClientTutorial { privat

JUnit + Mockito 单元测试(一)

未接触 JUnit 之前,曾经对茫茫的代码不知所措--哪怕是自己写的--多写注释?重构代码?甚至为一个方法去写一篇技术文章来解释?--这些都是试过,感觉不是"控制代码"的可行之道,甚至说"徒劳"的.关于单元测试(Unit test),之前亦略有所闻,感觉用处不大,因为对一个方法检测返回的结果是否正确,--有点无聊--心想,我写的方法当然能返回预期的结果,这还有说?不至于那么低级的错误也犯得着吧!?于是对所谓测试的东东感觉简直就是在增加工作量--我把代码写漂亮点就行了

测试框架 Mocha 实例教程

Mocha(发音"摩卡")诞生于2011年,是现在最流行的JavaScript测试框架之一,在浏览器和Node环境都可以使用. 所谓"测试框架",就是运行测试的工具.通过它,可以为JavaScript应用添加测试,从而保证代码的质量. 本文全面介绍如何使用Mocha,让你轻松上手.如果你以前对测试一无所知,本文也可以当作JavaScript单元测试入门.值得说明的是,除了Mocha以外,类似的测试框架还有Jasmine.Karma.Tape等,也很值得学习. 一.安

Xcode4 单元测试快速入门[转]

This is a post by iOS Tutorial Team member Unit testing is great because it makes your life easier. Easier to deliver high quality code, and easier to make changes without fear of breaking something! But what might not be so easy is getting started i

Java4Android之Java+Annotation入门

对java的Anotation还是不理解,希望看完这个贴子能够理解,Mark一下,明天再看.困了. http://wenku.baidu.com/link?url=eWzdJ4sDjnYA_OymO5JKBwmp97dhdZsCMpnnzN0G2l5eAVKRq7LujRCsIcRRt5B9ixVxDXLdEjycHvB5Kp7G3uLqaqJuF8fF9DgdRIdeLrS

grunt从入门到自定义项目模板

文章还可在我的github上找到,排版更友好一点:grunt从入门到自定义项目模板 一.Grunt入门介绍 1. Grunt是神马 基于任务的命令行构建工具(针对JavaScript项目) 链接:http://gruntjs.com/ 2. 使用Grunt的理由 前端的工具算得上是五花八门,在介绍如何Grunt之前,首先我们得反问自己: Grunt能够帮我们解决什么问题? 是否有其他更合适的替代方案? 3. Grunt能够帮我们解决什么问题? 作为一名开发人员,我们见过了不少功能胡里花哨但并不实

Eclipse平台入门之二:开发环境与实例

接着上一篇文章Eclipse平台入门之一:什么是Eclipse,我们将开始介绍Java 开发环境(JDE). 为试验一下 Java 开发环境,我们将创建并运行一个"Hello, world"应用程序.使用 Java 透视图,右键单击"Hello"项目,选择 New=>Class,如图 2 所示.在随后出现的对话框中,键入"Hello"作为类名称.在"Which method stubs would you like to crea

Java入门知识

What is Java? Why Java? 大家学习Java有各自的理由,基本上Java是一个比较好的面向对象语言,api丰富 是当前比较主流的编程语言,J2SE/J2ME/J2EE在各个开发领域都发展良好 Java不会取代其他开发语言或开发平台,如c/c++/c#或Delphi/.NET 没有最好的语言,只有某种应用场合下相对合适的语言 JRE Java 2 Runtime Environment, Java 运行时环境 包括Java虚拟机(JVM),和必须的类库 它是运行Java程序和网