知识大全 利用 Ant 和 JUnit 进行增量开发
Posted 知
篇首语:富有臂力的人只能战胜一人;富有知识的人却所向无敌。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 利用 Ant 和 JUnit 进行增量开发相关的知识,希望对你有一定的参考价值。
利用 Ant 和 JUnit 进行增量开发 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!
软件开发习惯中一个细微更改都可能会对软件质量产生巨大改进 将单元测试合并到开发过程中 然后从长远角度来看它可以节省多少时间和精力 本文通过使用代码样本说明了单元测试的种种好处 特别是使用 Ant 和 JUnit 带来的各种方便
测试是大型开发过程中的基本原则之一 在任何职业中 验证都是一个重要部分 医生要通过验血来确诊 波音公司在研制 的过程中对飞机的每个组件都进行了精心测试 为什么软件开发就应该例外呢?
以前 由于在应用程序中将 GUI 和商业逻辑紧密联系在一起 这就限制了创建自动测试的能力 当我们学会通过抽象层将商业逻辑从界面中分离出来时 各个单独代码模块的自动测试就替代了通过 GUI 进行的手工测试
现在 集成开发环境 (IDE) 能在您输入代码的同时显示错误 对于在类中快速查找方法具有智能探测功能 可以利用语法结构生成彩色代码 而且具有许多其它功能 因此 在编译更改过的代码之前 您已经全盘考虑了将构建的类 但您是否考虑过这样的修改会破坏某些功能呢?
每个开发者都碰到过更改 臭虫 代码修改过程可能会引入 臭虫 而如果通过用户界面手工测试代码的话 在编译完成之前是不会发现它的 然后 您就要花费几天的时间追踪由更改所引起的错误 最近在我做的一个项目中 当我把后端数据库由 Informix 更改到 Oracle 时就遇到了这种情况 大部分更改都十分顺利 但由于数据库层或使用数据库层的系统缺少单元测试 从而导致将大量时间花费在尝试解决更改 臭虫 上 我花了两天的时间查到别人代码中的一个数据库语法更改 (当然 那个人仍是我的朋友 )
尽管测试有许多好处 但一般的程序员对测试都不太感兴趣 开始时我也没有 您听到过多少次 它编译了 所以它一定能用 这种言论?但 我思 故我在 这种原则并 不 适用于高质量软件 要鼓励程序员测试他们的代码 过程必须简单无痛
本文从某人学习用 Java 语言编程时所写的一个简单的类开始 然后 我会告诉您我是如何为这个类编写单元测试 以及在编写完它以后又是如何将单元测试添加到构建过程中的 最后 我们将看到将 臭虫 引入代码时发生的情况
从一个典型类开始 第一个典型的 Java 程序一般都包含一个打印 Hello World 的 main() 在清单 中 我创建了一个 HelloWorld 对象的实例并调用 sayHello() 方法 该方法会打印这句习惯说法
清单 我的第一个 Java 应用程序 Hello world /* * HelloWorld java * My first java program */ class HelloWorld /** * Print Hello World */ void sayHello() System out println( Hello World ); /** * Test */ public static void main( String[] args ) HelloWorld world = new HelloWorld(); world sayHello();main() 方法是我的测试 哦噢!我将代码 文档 测试和样本代码包含在了一个模块中 保佑 Java!但随着程序越变越大 这种开发方法很快就开始显现出了缺陷
- 混乱 类接口越大 main() 就越大 类可能仅仅因为正常的测试而变得非常庞大
- 代码膨胀 由于加入了测试 所以产品代码比所需要的要大 但我不想交付测试 而只想交付产品
- 测试不可靠 既然 main() 是代码的一部分 main() 就对其他开发者通过类接口无法访问的私有成员和方法享有访问权 出于这个原因 这种测试方法很容易出错
- 很难自动测试 要进行自动测试 我仍然必须创建另一程序来将参数传递给 main()
类开发 对我来说 类开发是从编写 main() 方法开始的 我在编写 main() 的时候就定义类和类的用法 然后实现接口 它的一些明显的缺陷也开始显现出来 一个缺陷是我传递给 main() 来执行测试的参数个数 其次 main() 本身在进行调用子方法 设置代码等操作时变得很混乱 有时 main() 会比类实现的其余部分还要大
更简单的过程 我原来的做法有一些很明显的缺陷 因此 让我们看看有什么别的方法可以使问题简化 我仍然通过接口设计代码并给出应用示例 正如原来的 main() 一样 不同的是我将代码放到了另一个单独的类中 而这个类恰好是我的 单元测试 这种技术有以下几点好处
- 设计类的一种机制 因为是通过接口进行开发 所以不太可能利用类的内部功能 但因为我是目标类的开发者 我有到其内部工作的 窗口 所以测试并不是个真正的黑箱 仅凭这一点就足够推断出需要开发者本人在编写目标类的同时负责测试的开发 而不是由其他任何人代劳
- 类用法的示例 通过将示例从实现中分离出来 开发者可以更快地提高速度 而且再不用在源代码上纠缠不清 这种分离还有助于防止开发者利用类的内部功能 因为这些功能将来可能已经不存在了
- 没有类混乱的 main() 我不再受到 main() 的限制了 以前我得将多个参数传递给 main() 来测试不同的配置 现在我可以创建许多单独的测试类 每一个都维护各自的设置代码
接下来我们将这个单独的单元测试对象放入构建过程中 这样 我们就可以提供自动确认过程的方法
- 确保所做的任何更改都不会对其他人产生不利影响
- 我们在进行源码控制之前就可以测试代码 而无需等待汇编测试或在夜晚进行的构建测试 这有助于尽早捕捉到 臭虫 从而降低产生高质量代码的成本
- 通过提供增量测试过程 我们提供了更好的实现过程 如同 IDE 帮助我们在输入时捕捉到语法或编译 臭虫 一样 增量单元测试也帮助我们在构建时捕捉到代码更改 臭虫
使用 JUnit 自动化单元测试 要使测试自动化 您需要一个测试框架 您可以自己开发或购买 也可以使用某些开放源代码工具 例如 JUnit 我选择 JUnit 出于以下几个原因
- 不需要编写自己的框架
- 它是开放源代码 因此不需要购买框架
- 开放源代码社区中的其他开发者会使用它 因此可以找到许多示例
- 它可以让我将测试代码与产品代码分开
- 它易于集成到我的构建过程中
测试布局 图 显示了使用样本 TestSuite 的 JUnit TestSuite 布局 每个测试都由若干单独的测试案例构成 每个测试案例都是一个单独的类 它扩展了 TestClass 类并包含了我的测试代码 即那些曾在 main() 中出现的代码 在该例中 我向 TestSuite 添加了两个测试 一个是 SkeletonTest 我将它用作所有新类和 HelloWorld 类的起点
图 TestSuite 布局
测试类 HelloWorldTest java 按照约定 测试类的名称中包含我所测试的类的名称 但将 Test 附加到结尾 在本例中 我们的测试类是 HelloWorldTest java 我复制了 SkeletonTest 中的代码 并添加了 testSayHello() 来测试 sayHello() 请注意 HelloWorldTest 扩展了 TestCase JUnit 框架提供了 assert 和 assertEquals 方法 我们可以使用这些方法来进行验证 HelloWorldTest java 显示在清单 中
清单 HelloWorldTest java package test pany; import pany HelloWorld; import junit framework TestCase; import junit framework AssertionFailedError; /** * JUnit testcases for HelloWorld */ public class HelloWorldTest extends TestCase public HelloWorldTest(String name) super(name); public static void main(String args[]) junit textui TestRunner run(HelloWorldTest class); public void testSayHello() HelloWorld world = new HelloWorld(); assert( world!=null ); assertEquals( Hello World world sayHello() );testSayHello() 看上去和 HelloWorld java 中原来的 main 方法类似 但有一个主要的不同之处 它不是执行 System out println 并显示结果 而是添加了一个 assertEquals() 方法 如果两个值不同 assertEquals 将打印出两个输入的值 您可能已经注意到这个方法不起作用!HelloWorld 中的 sayHello() 方法不返回字符串 如果我先写过测试 就会捕捉到这一点 我将 Hello World 字符串与输出流联结起来 这样 按照清单 中显示的那样重写了 HelloWorld 去掉 main() 并更改了 sayHello() 的返回类型
清单 Hello world 测试案例 package pany; public class HelloWorld public String sayHello() return Hello World ;如果我保留了 main() 并修改了联系 代码看上去如下 public static void main( String[] args ) HelloWorld world = new HelloWorld(); System out println(world sayHello());
新的 main() 与我测试程序中的 testSayHello() 非常相似 是的 它看上去不象是一个现实世界中的问题(这是人为示例的问题) 但它说明了问题 在单独的应用程序中编写 main() 可以改进您的设计 同时帮助您设计测试 现在我们已经创建了一个测试类 让我们使用 Ant 来将它集成到构建中
使用 Ant 将测试集成到构建中 Jakarta Project 将 Ant 工具说成 不带 make 缺点的 make Ant 正在成为开放源代码世界中实际上的标准 原因很简单 Ant 是使用 Java 语言编写的 这种语言可以让构建过程在多种平台上使用 这种特性简化了在不同 OS 平台之间的程序员的合作 而合作是开放源代码社区的一种需要 您可以在自己选择的平台上进行开发 和 构建 Ant 的特性包括
- 类可扩展性 Java 类可用于扩展构建特性 而不必使用基于 shell 的命令
- 开放源代码 因为 Ant 是开放源代码 因此类扩展示例很充足 我发现通过示例来学习非常棒
- XML 可配置 Ant 不仅是基于 Java 的 它还使用 XML 文件配置构建过程 假设构建实际上是分层的 那么使用 XML 描述 make 过程就是其逻辑层 另外 如果您了解 XML 要学习如何配置构建就更简单一些
图 简要介绍了一个配置文件 配置文件由目标树构成 每个目标都包含了要执行的任务 其中任务就是可以执行的代码 在本例中 mkdir 是目标 pile 的任务 mkdir 是建立在 Ant 中的一个任务 用于创建目录 Ant 带有一套健全的内置任务 您也可以通过扩展 Ant 任务类来添加自己的功能
每个目标都有唯一的名称和可选的相关性 目标相关性需要在执行目标任务列表之前执行 例如图 所示 在执行 pile 目标中的任务之前需要先运行 JUNIT 目标 这种类型的配置可以让您在一个配置中有多个树
图 Ant XML 构建图
与经典 make 实用程序的相似性是非常显著的 这是理所当然的 因为 make 就是 make 但也要记住有一些差异 通过 Java 实现的跨平台和可扩展性 通过 XML 实现的可配置 还有开放源代码
下载和安装 Ant 首先下载 Ant(请参阅参考资料) 将 Ant 解压缩到 tools 目录 再将 Ant bin 目录添加到路径中 (在我的机器上是 e:\\tools\\ant\\bin )设置 ANT_HOME 环境变量 在 NT 中 这意味着进入系统属性 然后以带有值的变量形式添加 ANT_HOME ANT_HOME 应该设置为 Ant 根目录 即包含 bin 和 lib 目录的目录 (对我来说 是 e:\\tools\\ant )确保 JAVA_HOME 环境变量设置为安装了 JDK 的目录 Ant 文档有关于安装的详细信息
下载和安装 JUnit 下载 JUnit (请参阅参考资料) 解开 junit zip 并将 junit jar 添加到 CLASSPATH 如果将 junit zip 解包到类路径中 可以通过运行以下命令来测试安装 java junit textui TestRunner junit samples AllTests
定义目录结构 在开始我们的构建和测试过程之前 需要一个项目布局 图 显示了我的样本项目的布局 下面描述了布局的目录结构
- build 类文件的临时构建位置 构建过程将创建这个目录
- src 源代码的位置 Src 被分为 test 文件夹和 main 文件夹 前者用于所有的测试代码 而后者包含可交付的代码 将测试代码与主要代码分离提供了几点特性 首先 使主要代码中的混乱减少 其次 它允许包对齐 我就热衷与将类和与其相关的包放置在一起 测试就应该和测试在一起 它还有助于分发过程 因为你不可能打算将单元测试分发给客户
在实际中 我们有多个目录 例如 distribution 和 documentation 我们还会在 main 下有多个用于包的目录 例如 pany util
因为目录结构经常变动 所以在 build xml 中有这些变动的全局字符串常数是很重要的
图 项目布局图
Ant 构建配置文件示例 下一步 我们要创建配置文件 清单 显示了一个 Ant 构建文件示例 构建文件中的关键就是名为 runtests 的目标 这个目标进行分支判断并运行外部程序 其中外部程序是前面已安装的 junit textui TestRunner 我们指定要使用语句 test pany AllJUnitTests 来运行哪个测试套件
清单 构建文件示例 <property name= app name value= sample /> <property name= build dir value= build/classes /> <target name= JUNIT > <available property= junit present classname= junit framework TestCase /> </target> <target name= pile depends= JUNIT > <mkdir dir= $build dir /> <javac srcdir= src/main/ destdir= $build dir > <include name= **/* java /> </javac> </target> <target name= jar depends= pile > <mkdir dir= build/lib /> <jar jarfile= build/lib/$app name jar basedir= $build dir includes= /** /> </target> <target name= piletests depends= jar > <mkdir dir= build/testcases /> <javac srcdir= src/test destdir= build/testcases > <classpath> <pathelement location= build/lib/$app name jar /> <pathelement path= /> </classpath> <include name= **/* java /> </javac> </target> <target name= runtests depends= piletests if= junit present > <java fork= yes classname= junit textui TestRunner taskname= junit failonerror= true > <arg value= test pany AllJUnitTests /> <classpath> <pathelement location= build/lib/$app name jar /> <pathelement location= build/testcases /> <pathelement path= /> <pathelement path= $java class path /> </classpath> </java> </target> </project>运行 Ant 构建示例 开发过程中的下一步是运行将创建和测试 HelloWorld 类的构建 清单 显示了构建的结果 其中包括了各个目标部分 最酷的那部分是 runtests 输出语句 它告诉我们整个测试套件都正确运行了
我在图 和图 中显示了 JUnit GUI 其中所要做的就是将 runtest 目标从 junit textui TestRunner 改为 junit ui TestRunner 当您使用 JUnit 的 GUI 部分时 您必须选择退出按钮来继续构建过程 如果使用 Junit GUI 构建包 那么它将更难与大型的构建过程相集成 另外 文本输出也与构建过程更一致 并可以定向输出到一个用于主构建记录的文本文件 这对于每天晚上都要进行的构建非常合适
清单 构建输出示例 E:\\projects\\sample>ant runtests Searching for build xml Buildfile: E:\\projects\\sample\\build xml JUNIT: pile: [mkdir] Created dir: E:\\projects\\sample\\build\\classes [javac] Compiling source file to E:\\projects\\sample\\build\\classes jar: [mkdir] Created dir: E:\\projects\\sample\\build\\lib [jar] Building jar: E:\\projects\\sample\\build\\lib\\sample jar piletests: [mkdir] Created dir: E:\\projects\\sample\\build\\testcases [javac] Compiling source files to E:\\projects\\sample\\build\\testcases runtests: [junit] [junit] Time: [junit] [junit] OK ( tests) [junit] BUILD SUCCESSFUL Total time: second图 JUnit GUI 测试成功
图 JUnit GUI 测试失败
了解测试的工作原理 让我们搞点破坏 然后看看会发生什么事 夜深了 我们决定把 Hello World 变成一个静态字符串 在更改期间 我们 不小心 打错了字母 将 o 变成了 如清单 所示
清单 Hello world 类更改 package pany; public class HelloWorld private final static String HELLO_WORLD = Hell World ; public String sayHello() return HELLO_WORLD;在构建包时 我们看到了错误 清单 显示了 runtest 中的错误 它显示了失败的测试类和测试方法 并说明了为什么会失败 我们返回到代码中 改正错误后离开
清单 构建错误示例 E:\\projects\\sample>ant runtests Searching for build xml Buildfile: E:\\projects\\sample\\build xml JUNIT: pile: jar: piletests: runtests: [junit] F [junit] Time: [junit] [junit] FAILURES!!! [junit] Test Results: [junit] Run: Failures: Errors: [junit] There was failure: [junit] ) testSayHello(test pany HelloWorldTest) expected:<Hello World> but was:<Hell World> [junit] BUILD FAILED E:\\projects\\sample\\build xml: : Java returned: Total time: seconds并非完全无痛 新的过程并不是完全无痛的 为使单元测试成为开发的一部分 您必须采取以下几个步骤
- 下载和安装 JUnit
- 下载和安装 Ant
- 为构建创建单独的结构
- 实现与主类分开的测试类
- 学习 Ant 构建过程
但好处远远超过了痛苦 通过使单元测试成为开发过程的一部分 您可以
- 自动验证以捕捉更改 臭虫
- 从接口角度设计类
- 提供干净的示例
- 在发行包中避免代码混乱和类膨胀
实现 x 保证产品的质量要花费很多钱 但如果质量有缺陷 花费的钱就更多 如何才能使所花的钱获得最大价值 来保证产品质量呢?
- 评审设计和代码 评审可以达到的效果是单纯测试的一半
- 通过单元测试来确认模块可以使用 尽管测试早就存在 但随着开发实践的不断发展 单元测试逐渐成为日常开发过程的一个部分
在我 年的开发生涯里 为 emageon 工作是最重要的部分之一 在 emageon 时 设计评审 代码评审和单元测试是每天都要做的事 这种日常开发习惯造就了最高质量的产品 软件在客户地点第一年的当机次数为零 是一个真正的 x 产品 单元测试就象刷牙 您不一定要做 但如果做了 生活质量就更好
- 您可以参阅本文在 developerWorks 全球站点上的 英文原文
- 下载在本文中引用的 示例代码
- 从 Apache 网站下载 Ant 如需 Ant 文档 FAQ 和其他下载 请访问 Jakarta 项目的Ant 主页
- JUnit 主页 提供了额外的测试示例 文档 文章和 FAQ 您可以从 xprogramming 下载 JUnit
- Kent Beck 所写的 简单的 Smalltalk 测试 (Simple Smalltalk Testing) 讨论了一个简单的测试策略和支持它的框架
- 请参阅其它开发者的有关单元测试的评论 (ments on unit testing)
- 要了解其它有用的开发习惯 请访问 终极编程主页 (Extreme Programming Home page)
相关参考
Java应用利器组合:Ant+JUnit+Cobertura 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看
知识大全 使用Ant编译、运行Junit、以及检查编码[3]
使用Ant编译、运行Junit、以及检查编码[3] 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!&
知识大全 使用Ant编译、运行Junit、以及检查编码[2]
使用Ant编译、运行Junit、以及检查编码[2] 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!&
知识大全 使用Ant编译、运行Junit、以及检查编码[1]
使用Ant编译、运行Junit、以及检查编码[1] 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!&
知识大全 使用Ant编译、运行Junit、以及检查编码[4]
使用Ant编译、运行Junit、以及检查编码[4] 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!&
使用Ant进行Web开发,第一部分 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 本文是Ant:
知识大全 利用命令进行简单的增量文件夹备份(win/linux)
这两天突然看到一个需求点需要实现增量备份的功能找了一些资料记录一下如下命令该命令可以利用命令行进行简单的增量备份功能Win 复制代码代码如下: xcopyC:\\DocsD:\\Bac
Jython开发的JUnit测试包 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! JUnit测试
使用ANT协助高效开发java项目 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! ANT是Apa
开发方法及软件工程之Ant之文件操作[3] 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!