`

JUnit 4 初体验

阅读更多
测试任何可能的错误。单元测试不是用来证明您是对的,而是为了证明您没有错。
.

麻雀虽小,五脏俱全。JUnit 设计的非常小巧,但是功能却非常强大。Martin Fowler 如此评价 JUnit:在软件开发领域,从来就没有如此少的代码起到了如此重要的作用。它大大简化了开发人员执行单元测试的难度,特别是 JUnit 4 使用 Java 5 中的注解(annotation)使测试变得更加简单。
在开始体验 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)。
请注意 JDK 的版本JUnit 4.1 是基于 Java 5 的升级版本,它使用了 Tiger 中的很多新特性来简化原有的使用方式。正因为如此,它并不能直接运行在 JDK1.4.x 版本上。如果您需要在 JDK1.4.x 版本使用 JUnit 的话,请使用 3.8.1 版本。
.

图 2 修改项目源代码目录


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

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

 import java.util.regex.Matcher; 
 import java.util.regex.Pattern; 

 /** 
 * 对名称、地址等字符串格式的内容进行格式检查
 * 或者格式化的工具类
 * 
 * @author Ai92 
 */ 
 public class WordDealUtil { 

	 /** 
	 * 将 Java 对象名称(每个单词的头字母大写)按照
	 * 数据库命名的习惯进行格式化
	 * 格式化后的数据为小写字母,并且使用下划线分割命名单词
	 * 
	 * 例如:employeeInfo 经过格式化之后变为 employee_info 
	 * 
	 * @param name 	 Java 对象名称
	 */ 
	 public static String wordFormat4DB(String name){ 
		 Pattern p = Pattern.compile("[A-Z]"); 
		 Matcher m = p.matcher(name); 
		 StringBuffer sb = new StringBuffer(); 
		
		 while(m.find()){ 
			 m.appendReplacement(sb, "_"+m.group()); 
		 } 
		 return m.appendTail(sb).toString().toLowerCase(); 
	 } 
 } 
 



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

 import static org.junit.Assert.assertEquals; 
 import org.junit.Test; 

 public class TestWordDealUtil { 
	 // 测试 wordFormat4DB 正常运行的情况
	 @Test public void wordFormat4DBNormal(){ 
		 String target = "employeeInfo"; 
		 String result = WordDealUtil.wordFormat4DB(target); 
		
		 assertEquals("employee_info", result); 
	 } 
 } 
  




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

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

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

绿色的进度条提示我们,测试运行通过了。但现在就宣布代码通过了单元测试还为时过早。记住:您的单元测试代码不是用来证明您是对的,而是为了证明您没有错。因此单元测试的范围要全面,比如对边界值、正常值、错误值得测试;对代码可能出现的问题要全面预测,而这也正是需求分析、详细设计环节中要考虑的。显然,我们的测试才刚刚开始,继续补充一些对特殊情况的测试:
 public class TestWordDealUtil { 
	……
	 // 测试 null 时的处理情况
	 @Test public void wordFormat4DBNull(){ 
		 String target = null; 
		 String result = WordDealUtil.wordFormat4DB(target); 
		
		 assertNull(result); 
	 } 
	
	 // 测试空字符串的处理情况
	 @Test public void wordFormat4DBEmpty(){ 
		 String target = ""; 
		 String result = WordDealUtil.wordFormat4DB(target); 
		
		 assertEquals("", result); 
	 } 

	 // 测试当首字母大写时的情况
	 @Test public void wordFormat4DBegin(){ 
		 String target = "EmployeeInfo"; 
		 String result = WordDealUtil.wordFormat4DB(target); 
		
		 assertEquals("employee_info", result); 
	 } 
	
	 // 测试当尾字母为大写时的情况
	 @Test public void wordFormat4DBEnd(){ 
		 String target = "employeeInfoA"; 
		 String result = WordDealUtil.wordFormat4DB(target); 
		
		 assertEquals("employee_info_a", result); 
	 } 
	
	 // 测试多个相连字母大写时的情况
	 @Test public void wordFormat4DBTogether(){ 
		 String target = "employeeAInfo"; 
		 String result = WordDealUtil.wordFormat4DB(target); 
		
		 assertEquals("employee_a_info", result); 
	 } 
 } 
 
 



再次运行测试。很遗憾,JUnit 运行界面提示我们有两个测试情况未通过测试(图 4)——当首字母大写时得到的处理结果与预期的有偏差,造成测试失败(failure);而当测试对 null 的处理结果时,则直接抛出了异常——测试错误(error)。显然,被测试代码中并没有对首字母大写和 null 这两种特殊情况进行处理,修改如下:
 // 修改后的方法 wordFormat4DB 
 /** 
	 * 将 Java 对象名称(每个单词的头字母大写)按照
	 * 数据库命名的习惯进行格式化
	 * 格式化后的数据为小写字母,并且使用下划线分割命名单词
	 * 如果参数 name 为 null,则返回 null 
	 * 
	 * 例如:employeeInfo 经过格式化之后变为 employee_info 
	 * 
	 * @param name Java 对象名称
	 */ 
	 public static String wordFormat4DB(String name){ 
		
		 if(name == null){ 
			 return null; 
		 } 
		
		 Pattern p = Pattern.compile("[A-Z]"); 
		 Matcher m = p.matcher(name); 
		 StringBuffer sb = new StringBuffer(); 
		
		 while(m.find()){ 
			 if(m.start() != 0) 
				 m.appendReplacement(sb, ("_"+m.group()).toLowerCase()); 
		 } 
		 return m.appendTail(sb).toString().toLowerCase(); 
	 } 
 
 





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

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

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


http://www.ibm.com/developerworks/cn/java/j-lo-junit4/
分享到:
评论

相关推荐

    JUnit4初体验

    《JUnit4初体验》 JUnit4是Java编程语言中广泛使用的单元测试框架,它极大地简化了测试代码的编写,使得开发者能够更加专注于功能实现,同时确保代码的质量。本篇文章将带你初探JUnit4的魅力,了解其核心概念和使用...

    Easyb初体验

    【标题】:“Easyb初体验” 在IT行业中,我们经常需要进行各种自动化测试,以确保软件的质量和稳定性。Easyb就是这样一款工具,它为Java应用程序提供了简洁、易读的BDD(行为驱动开发)风格的测试框架。这篇博客将...

    URLClassLoader初体验

    这篇博客文章“URLClassLoader初体验”可能深入探讨了如何使用`URLClassLoader`来动态加载类和资源,以及它在实际开发中的应用。下面我们将详细探讨`URLClassLoader`的相关知识点。 首先,`URLClassLoader`是Java...

    最新IDEA, 2024.1.4 Windows版

    IntelliJ IDEA是一款由JetBrains公司开发的...总之,IntelliJ IDEA 2024.1.4 Windows版将带来一系列改进和新功能,旨在提升开发者的效率和舒适度,无论你是经验丰富的开发者还是初入编程的新手,这个版本都值得尝试。

    安卓开发-weibo4andriod-2011-01-14.zip.zip

    本篇文章将围绕"安卓开发-weibo4andriod-2011-01-14.zip.zip"这一主题,深入探讨Weibo4Android项目在2011年初的开发细节,旨在帮助读者理解安卓平台上的微博应用开发,以及当时的开发环境和技术趋势。 Weibo4...

    Hibernate1

    对 Hibernate 初体验 1.新建一个java工程,工程名为Hiberante1 2.导入相应的jar包 1)hibernate需要导入这些包 2)因为我们需要操作数据库所以要导入数据库的包,我用的mysql数据库 3.创建数据库以及表 ...

    Dagger2 Demo

    在"Dagger2 初体验.md"这个文件中,我们可能会找到以下关键概念和步骤: 1. **组件(Component)**:组件是Dagger2的核心,它定义了依赖关系的接口。通过`@Component`注解,我们可以声明所需的模块(Module)以及...

    测试基础笔记

    达内科技的测试基础笔记为初入行业的测试人员提供了丰富的学习资源,旨在帮助他们掌握基本的测试理论和实践技能。下面我们将深入探讨一些核心的测试知识点。 一、测试目标与原则 1. 测试的目标是为了发现软件中的...

    搞定J2EE核心技术与企业应用:Ajax,JSP,Struts2,Spring,Hibernate,完整扫描版

    Log4j用于日志记录,JUnit用于单元测试,JFreeChart则是用于生成图表的库,这些工具对于提升软件开发的效率和软件质量至关重要。 为了进一步增强实战性,本书还介绍了如何将这些技术整合起来,并通过不同的项目案例...

    (picture)相册管理

    对于初入编程领域的人士来说,这是一个理想的参考资料,可以从中学习到如何构建一个功能完备的相册管理系统。 【描述】:这个相册系统包含了从数据存储、图像处理到用户交互等多个方面的编程知识。通过分析和理解...

    软件测试 教程 经典 入门 提高

    在信息技术领域,软件测试是一项至关重要的环节,它确保了产品的质量和用户体验。这个“软件测试教程”旨在为初学者提供一个全面的入门平台,并帮助有经验的测试人员进一步提升技能。本文将深入探讨软件测试的基础...

    Ajax基础教程.doc

    然而,真正推动Ajax发展的是2000年代初Google Maps的发布,它利用JavaScript与服务器进行异步通信,实现了平滑的滚动和缩放效果,从而展示了Ajax的潜力。 **2. XMLHttpRequest对象** XMLHttpRequest对象是Ajax的...

    Java Testing and Design(English)

    随着业务的发展,系统面临的负载会逐渐增加,因此,设计之初就需要考虑系统的可扩展性,确保在负载增加时,系统能够平滑地扩展资源,保持稳定的服务质量。这涉及到微服务架构、负载均衡、缓存策略等一系列技术的应用...

    Java精品项目第41期毕业设计管理系统

    例如,使用Log4j进行日志记录,JUnit进行单元测试,以及使用Git进行版本控制和团队协作。 总的来说,【Java精品项目第41期毕业设计管理系统】是一个全面的实践项目,它涵盖了Java Web开发的多个重要方面,包括后端...

    软件评测师课件.rar

    软件评测师是软件开发过程中的关键参与者,他们通过系统化的方法对软件进行测试,发现并报告问题,以提高软件的可靠性和用户体验。他们的主要职责包括制定测试计划,设计测试用例,执行测试,记录并分析结果,以及...

    让Struts 1焕发青春----小议对Struts的改造.

    4. **单元测试**:利用JUnit和Mockito等工具,编写单元测试,保证代码质量。 5. **自动化构建**:集成Maven或Gradle,实现项目的自动化构建、部署和测试。 总结来说,通过对Struts 1的性能优化、安全加固以及现代...

Global site tag (gtag.js) - Google Analytics