MLDN
课程咨询[孔老师]QQ 1031143579孔老师QQ课程咨询留言 课程咨询[徐老师 ]QQ 945313230
徐老师QQ课程咨询留言
课程咨询[刘老师 ]QQ 514543793
刘老师QQ课程咨询留言
java培训
北京java培训
IT电子教育门户 高端JAVA培训 -=> 资源中心 -=> JAVA SE技术专区 -=> JavaGUI编程 -=> 正文

利用 Ant 和 JUnit 进行增量开发(上)

 发布日期:2007-12-5 8:47:00 发布者:[IT电子教育门户]   评论:[]  浏览:

  软件开发习惯中一个细微更改都可能会对软件质量产生巨大改进。将单元测试合并到开发过程中,然后从长远角度来看它可以节省多少时间和精力。本文通过使用代码样本说明了单元测试的种种好处,特别是使用 Ant 和 JUnit 带来的各种方便。

  测试是大型开发过程中的基本原则之一。在任何职业中,验证都是一个重要部分。医生要通过验血来确诊。波音公司在研制 777 的过程中对飞机的每个组件都进行了精心测试。为什么软件开发就应该例外呢?

  以前,由于在应用程序中将 GUI 和商业逻辑紧密联系在一起,这就限制了创建自动测试的能力。当我们学会通过抽象层将商业逻辑从界面中分离出来时,各个单独代码模块的自动测试就替代了通过 GUI 进行的手工测试。

  现在,集成开发环境 (IDE) 能在您输入代码的同时显示错误,对于在类中快速查找方法具有智能探测功能,可以利用语法结构生成彩色代码,而且具有许多其它功能。因此,在编译更改过的代码之前,您已经全盘考虑了将构建的类,但您是否考虑过这样的修改会破坏某些功能呢?

  每个开发者都碰到过更改“臭虫”。代码修改过程可能会引入“臭虫”,而如果通过用户界面手工测试代码的话,在编译完成之前是不会发现它的。然后,您就要花费几天的时间追踪由更改所引起的错误。最近在我做的一个项目中,当我把后端数据库由 Informix 更改到 Oracle 时就遇到了这种情况。大部分更改都十分顺利,但由于数据库层或使用数据库层的系统缺少单元测试,从而导致将大量时间花费在尝试解决更改“臭虫”上。我花了两天的时间查到别人代码中的一个数据库语法更改。(当然,那个人仍是我的朋友。)

  尽管测试有许多好处,但一般的程序员对测试都不太感兴趣,开始时我也没有。您听到过多少次“它编译了,所以它一定能用”这种言论?但“我思,故我在”这种原则并不适用于高质量软件。要鼓励程序员测试他们的代码,过程必须简单无痛。

  本文从某人学习用 Java 语言编程时所写的一个简单的类开始。然后,我会告诉您我是如何为这个类编写单元测试,以及在编写完它以后又是如何将单元测试添加到构建过程中的。最后,我们将看到将“臭虫”引入代码时发生的情况。

  从一个典型类开始

  第一个典型的 Java 程序一般都包含一个打印 "Hello World" 的 main()。在清单 1 中,我创建了一个 HelloWorld 对象的实例并调用 sayHello() 方法,该方法会打印这句习惯说法。

  清单 1. 我的第一个 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 出于以下几个原因:

  不需要编写自己的框架。

  它是开放源代码,因此不需要购买框架。

  开放源代码社区中的其他开发者会使用它,因此可以找到许多示例。

  它可以让我将测试代码与产品代码分开。

  它易于集成到我的构建过程中。

  测试布局

 图 1 显示了使用样本 TestSuite 的 JUnit TestSuite 布局。每个测试都由若干单独的测试案例构成。每个测试案例都是一个单独的类,它扩展了 TestClass 类并包含了我的测试代码,即那些曾在 main() 中出现的代码。在该例中,我向 TestSuite 添加了两个测试:一个是 SkeletonTest,我将它用作所有新类和 HelloWorld 类的起点。

  图 1. TestSuite 布局

  TestSuite 布局

  测试类 HelloWorldTest.java

  按照约定,测试类的名称中包含我所测试的类的名称,但将 Test 附加到结尾。在本例中,我们的测试类是 HelloWorldTest.java.我复制了 SkeletonTest 中的代码,并添加了 testSayHello() 来测试 sayHello()。请注意 HelloWorldTest 扩展了 TestCase.JUnit 框架提供了 assert 和 assertEquals 方法,我们可以使用这些方法来进行验证。HelloWorldTest.java 显示在清单 2 中。

  清单 2. HelloWorldTest.java

package test.com.company;

import com.company.HelloWorld;
import junit.framework.TestCase;
import junit.framework.AssertionFailedError;

/**
 * JUnit 3.2 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" 字符串与输出流联结起来。这样,按照清单 3 中显示的那样重写了 HelloWorld,去掉 main(),并更改了 sayHello() 的返回类型。

  清单 3. Hello world 测试案例。

package com.company;

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 来将它集成到构建中。

java视频教程
JAVA核心_75对象序列化练习
 JAVA核心_75对象序列化..
JAVA核心_74对象序列化
 JAVA核心_74对象序列化..
JAVA核心_73字符编码
 JAVA核心_73字符编码 ..
JAVA核心_72Scanner
 JAVA核心_72Scann..
JAVA核心_71 IO练习 二
 JAVA核心_71 IO练习..
相关文章 推荐文章
利用 Ant 和 JUnit 进..[12.5]
利用 Ant 和 JUnit 进..[12.5]
深入体会Ruby语言中的String类..
Java嵌入式开发之Kjava GUI..
利用 Ant 和 JUnit 进行增量..
利用 Ant 和 JUnit 进行增量..
利用 Ant 和 JUnit 进行增量..
热门文章
GUI只打开一个窗口实例的方法
使用Swing Worker线程 --..
Java3D理解初级教程
Java Swing中的键盘事件处理
在 Java 应用程序中创建图像
Swing是MVC设计的典范
线程与Swing
分享用Java3D做的3D粒子特效
主从按钮设计
Java中static、this、su..
今日更新
深入Java布局管理器
深入体会Ruby语言中的String类..
Java嵌入式开发之Kjava GUI..
利用 Ant 和 JUnit 进行增量..
利用 Ant 和 JUnit 进行增量..
利用 Ant 和 JUnit 进行增量..
反射在Java Swing中的应用
使用Java语言快速开发Linux G..
利用非正式特性创新Swing技术六大技..
Java Swing的基础知识全接触
JAVA招聘网
 评一评
正在读取…
  姓名:
  评论:
    
【注】 发表评论必需遵守以下条例: !!!
  • 尊重网上道德,遵守中华人民共和国的各项有关法律法规
  • 承担一切因您的行为而直接或间接导致的民事或刑事法律责任
  • 本站管理人员有权保留或删除其管辖留言中的任意内容
  • 本站有权在网站内转载或引用您的评论
  • 参与本评论即表明您已经阅读并接受上述条款
关于我们 | 商务合作 | 招聘信息 | 客服中心 | 服务条款 | 免责声明 | 网站导航 QQ留言
Copyright 2009 魔乐培训MLDN.CN all rights reserved 版权所有 京ICP备07008611号