`
bruce198
  • 浏览: 236338 次
  • 性别: Icon_minigender_1
社区版块
存档分类
最新评论

(转)JUnit实战篇 (二)

    博客分类:
  • java
阅读更多

                                                                             修订者:黄 凯        <o:p></o:p>

E_mail:hk_sz@163.com<o:p></o:p>

三、安装 <o:p></o:p>

1.      获取JUnit的软件包,从Junit(http://www.junit.org/index.htmhttp://download.sourceforge.net/junit/)下载最新的软件包。这里我使用的是http://download.sourceforge.net/junit/junit2.zip。<o:p></o:p>

2.      将其在适当的目录下解包(我安装在D:\junit2)。这样在安装目录(也就是你所选择的解包的目录)下你找到一个名为junit.jar的文件。将这个jar文件加入你的CLASSPATH系统变量。(IDE的设置会有所不同,参看你所喜爱的IDE的配置指南)JUnit就安装完了。<o:p></o:p>

<o:p> </o:p>

四、运行<o:p></o:p>

    通过前面的介绍,我们对JUnit有了一个大概的轮廓。知道了它是干什么的。现在让我们动手改写上面的测试类testCar使其符合Junit的规范--能在JUnit中运行。

  1. //执行测试的类(JUnit版)    
  2.   
  3. import junit.framework.*;    
  4.   
  5. public class testCar extends TestCase    
  6. {    
  7.     protected int expectedWheels;    
  8.     protected Car myCar;     
  9.     public testCar(String name)    
  10.     {    
  11.         super(name);    
  12.     }    
  13.   
  14.     protected void setUp()    
  15.     {    
  16.         expectedWheels = 4;    
  17.         myCar = new Car();    
  18.     }    
  19.   
  20.     public static Test suite()    
  21.     {    
  22.         /** the type safe way */  
  23.   
  24.         /*   
  25.         TestSuite suite= new TestSuite();   
  26.         suite.addTest(   
  27.             new testCar("Car.getWheels")   
  28.             {     
  29.                 protected void runTest()   
  30.                 {   
  31.                     testGetWheels();   
  32.                 }   
  33.             }   
  34.         );   
  35.  
  36.         return suite;  
  37.         */    
  38.         /** the dynamic way */    
  39.         return new TestSuite(testCar.class);    
  40.     }    
  41.   
  42.     public void testGetWheels()    
  43.     {    
  44.         assertEquals(expectedWheels, myCar.getWheels());    
  45.     }    
  46.   
  47. }   
  48.   

改版后的testCar已经面目全非。先让我们了解这些改动都是什么含义,再看如何执行这个测试。 <o:p></o:p>

1>import语句,引入JUnit的类。(没问题吧) <o:p></o:p>

2>继承 TestCase 。可以暂时将一个TestCase看作是对某个类进行测试的方法的集合。详细介绍请参看JUnit资料 <o:p></o:p>

3>setUp()设定了进行初始化的任务。我们以后会看到setUp会有特别的用处。 <o:p></o:p>

4>testGetWheeels()对预期的值和myCar.getWheels()返回的值进行比较,并打印比较的结果。assertEquals是junit.framework.Assert中所定义的方法,junit.framework.TestCase继承了junit.framework.Assert。 <o:p></o:p>

5>suite()是一个很特殊的静态方法。JUnit的TestRunner会调用suite方法来确定有多少个测试可以执行。上面的例子显示了两种方法:静态的方法是构造一个内部类,并利用构造函数给该测试命名(test name, 如 Car.getWheels),其覆盖的runTest()方法,指明了该测试需要执行那些方法--testGetWheels()。动态的方法是利用内省(reflection)来实现runTest(),找出需要执行那些测试。此时测试的名字即是测试方法(test method,如testGetWheels)的名字。JUnit会自动找出并调用该类的测试方法。 <o:p></o:p>

6>将TestSuite看作是包裹测试的一个容器。如果将测试比作叶子节点的话,TestSuite就是分支节点。实际上TestCase,TestSuite以及TestSuite组成了一个composite Pattern。JUnit的文档中有一篇专门讲解如何使用Pattern构造Junit框架。有兴趣的朋友可以查看JUnit资料。 <o:p></o:p>

如何运行该测试呢?手工的方法是键入如下命令: <o:p></o:p>

[Windows] D:\>java junit.textui.TestRunner testCar<o:p></o:p>

[Unix] % java junit.textui.TestRunner testCar <o:p></o:p>

别担心你要敲的字符量,以后在IDE中,只要点几下鼠标就成了。运行结果应该如下所示,表明执行了一个测试,并通过了测试: <o:p></o:p>

. <o:p></o:p>

Time: 0 <o:p></o:p>

OK (1 tests) <o:p></o:p>

如果我们将Car.getWheels()中返回的的值修改为3,模拟出错的情形,则会得到如下结果: <o:p></o:p>

.F<o:p></o:p>

Time: 0.16<o:p></o:p>

<o:p> </o:p>

FAILURES!!!<o:p></o:p>

Test Results:<o:p></o:p>

Run: 1 Failures: 1 Errors: 0<o:p></o:p>

There was 1 failure:<o:p></o:p>

1)      testCar.testGetWheels "expected:<3> but was:<4>"<o:p></o:p>

注意:Time上的小点表示测试个数,如果测试通过则显示OK。否则在小点的后边标上F,表示该测试失败。注意,在模拟出错的测试中,我们会得到详细的测试报告“expected:<3> but was:<4>”,这足以告诉我们问题发生在何处。下面就是你调试,测试,调试,测试...的过程,直至得到期望的结果。 <o:p></o:p>

<o:p> </o:p>

五、Design by Contract(这句话我没法翻译) <o:p></o:p>

Design by Contract本是Bertrand Meyer(Eiffel语言的创始人)开发的一种设计技术。我发现在JUnit中使用Design by Contract会带来意想不到的效果。Design by Contract的核心是断言(assersion)。断言是一个布尔语句,该语句不能为假,如果为假,则表明出现了一个bug。Design by Contract使用三种断言:前置条件(pre-conditions)、后置条件(post-conditions)和不变式(invariants)这里不打算详细讨论Design by Contract的细节,而是希望其在测试中能发挥其作用。 <o:p></o:p>

     前置条件在执行测试之前可以用于判断是否允许进入测试,即进入测试的条件。如 expectedWheels > 0, myCar != null。后置条件用于在测试执行后判断测试的结果是否正确。如 expectedWheels==myCar.getWheels()。而不变式在判断交易(Transaction)的一致性(consistency)方面尤为有用。我希望JUnit可以将Design by Contract作为未来版本的一个增强。 <o:p></o:p>

<o:p> </o:p>

六、Refactoring(这句话我依然没法翻译) <o:p></o:p>

     Refactoring本来与测试没有直接的联系,而是与软件熵有关,但既然我们说测试能解决软件熵问题,我们也就必须说出解决之道。(仅仅进行测试只能发现软件熵,Refactoring则可解决软件熵带来的问题。)软件熵引出了一个问题:是否需要重新设计整个软件的结构?理论上应该如此,但现实不允许我们这么做。这或者是由于时间的原因,或者是由于费用的原因。重新设计整个软件的结构会给我们带来短期的痛苦。而不停地给软件打补丁甚至是补丁的补丁则会给我们带来长期的痛苦。(不管怎样,我们总处于水深火热之中) <o:p></o:p>

     Refactoring是一个术语,用于描述一种技术,利用这种技术我们可以免于重构整个软件所带来的短期痛苦。当你refactor时,你并不改变程序的功能,而是改变程序内部的结构,使其更易理解和使用。如:该变一个方法的名字,将一个成员变量从一个类移到另一个类,将两个类似方法抽象到父类中。所作的每一个步都很小,然而1-2个小时的Refactoring工作可以使你的程序结构更适合目前的情况。Refactoring有一些规则: <o:p></o:p>

1> 不要在加入新功能的同时refactor已有的代码。在这两者间要有一个清晰的界限。如每天早上1-2个小时的Refactoring,其余时间添加新的功能;<o:p></o:p>

2> 在你开始Refactoring前,和Refactoring后都要保证测试能顺利通过,否则Refactoring没有任何意义;<o:p></o:p>

3> 进行小的Refactoring,大的就不是Refactoring了。如果你打算重构整个软件,就没有必要Refactoring了。只有在添加新功能和调试bug时才又必要Refactoring。不要等到交付软件的最后关头才Refactoring。那样和打补丁的区别不大。Refactoring 用在回归测试中也能显示其威力。要明白,我不反对打补丁,但要记住打补丁是应该最后使用的必杀绝招。(打补丁也需要很高的技术,详情参看微软网站) <o:p></o:p>

<o:p> </o:p>

七、IDE对JUnit的支持 <o:p></o:p>

目前支持JUnit的Java IDE 包括 <o:p></o:p>

IDE<o:p></o:p>

方式<o:p></o:p>

分数(1-5,满分5)<o:p></o:p>

Forte for Java 3.0 Enterprise Edition<o:p></o:p>

plug-in<o:p></o:p>

3<o:p></o:p>

Jbuilder 9 Enterprise Edition<o:p></o:p>

integrated with IDE<o:p></o:p>

4<o:p></o:p>

Visual Age for Java<o:p></o:p>

support<o:p></o:p>

N/A<o:p></o:p>

     在IDE中如何使用JUnit,是非常具体的事情。不同的IDE有不同的使用方法。一旦理解了JUnit的本质,使用起来就十分容易了。所以我们不依赖于具体的IDE,而是集中精力讲述如何利用JUnit编写单元测试代码。心急的人可参看资料。 <o:p></o:p>

<o:p> </o:p>

八、小结<o:p></o:p>

     你一旦安装完JUnit,就有可能想试试我们的Car和testCar类,没问题,我已经运行过了,你得到的结果应该和我列出的结果类似。接下来,你可能会先写测试代码,再写工作代码,或者相反,先写工作代码,再写测试代码。我更赞成使用前一种方法:先写测试代码,再写工作代码。因为这样可以使我们编写工作代码时清晰地了解工作类的行为。 <o:p></o:p>

     要注意编写一定能通过的测试代码(如文中的例子)并没有任何意义,只有测试代码能帮助我们发现bug,测试代码才有其价值。此外测试代码还应该对工作代码进行全面的测试。如给方法调用的参数传入空值、错误值和正确的值,看看方法的行为是否如你所期望的那样。 <o:p></o:p>

你现在已经知道了编写测试类的基本步骤: <o:p></o:p>

1> 扩展TestCase类; <o:p></o:p>

2> 覆盖runTest()方法(可选); <o:p></o:p>

3> 写一些testXXXXX()方法。<o:p></o:p>

<o:p> </o:p>

Fixture <o:p></o:p>

     接下来的问题是,如果你要对一个或若干个的类执行多个测试,该怎么办?JUnit对此有特殊的解决办法。如果需要在一个或若干个的类执行多个测试,这些类就成为了测试的context。在JUnit中被称为Fixture(如testCar类中的 myCar 和 expectedWheels )。当你编写测试代码时,你会发现你花费了很多时间配置/初始化相关测试的Fixture。将配置Fixture的代码放入测试类的构造方法中并不可取,因为我们要求执行多个测试,我并不希望某个测试的结果意外地(如果这是你要求的,那就另当别论了)影响其他测试的结果。通常若干个测试会使用相同的Fixture,而每个测试又各有自己需要改变的地方。为此,JUnit提供了两个方法,定义在TestCase类中。 <o:p></o:p>

protected void setUp() throws java.lang.Exception <o:p></o:p>

protected void tearDown() throws java.lang.Exception <o:p></o:p>

     覆盖setUp()方法,初始化所有测试的Fixture(你甚至可以在setUp中建立网络连接),将每个测试略有不同的地方在testXXX()方法中进行配置。覆盖tearDown()(我总想起一首叫雨滴的吉他曲),释放你在setUp()中分配的永久性资源,如数据库连接。当JUnit执行测试时,它在执行每个testXXXXX()方法前都调用setUp(),而在执行每个testXXXXX()方法后都调用tearDown()方法,由此保证了测试不会相互影响。 <o:p></o:p>

<o:p> </o:p>

TestCase <o:p></o:p>

     需要提醒一下,在junit.framework.Assert类中定义了相当多的assert方法,主要有assert(),assertEquals(), assertNull(), assertSame(), assertTrue(), fail()等方法。如果你需要比较自己定义的类,如Car。assert方法需要你覆盖Object类的equals()方法,以比较两个对象的不同。实践表明:如果你覆盖了Object类的equals()方法,最好也覆盖Object类的hashCode()方法。再进一步,连带Object类的toString()方法也一并覆盖。这样可以使测试结果更具可读性。 <o:p></o:p>

     当你设置好了Fixture后,下一步是编写所需的testXXX()方法。一定要保证testXXX()方法的public属性,否则无法通过内省(reflection)对该测试进行调用。每个扩展的TestCase类(也就是你编写的测试类)会有多个testXXX()方法。一个testXXX()方法就是一个测试。要想运行这个测试,你必须定义如何运行该测试。如果你有多个testXXX()方法,你就要定义多次。JUnit支持两种运行单个测试的方法:静态的和动态的方法。 <o:p></o:p>

     静态的方法就是覆盖TestCase类的runTest()方法,一般是采用内部类的方式创建一个测试实例: <o:p></o:p>

<o:p></o:p>

采用静态的方法要注意要给每个测试一个名字(这个名字可以任意起,但你肯定希望这个名字有某种意义),这样你就可以区分那个测试失败了。 <o:p></o:p>

  1. TestCase test01 = new testCar("test getWheels")    
  2. {    
  3.     public void runTest()    
  4.     {    
  5.         testGetWheels();    
  6.     }    
  7. }   

    

     动态的方法是用内省(Introspection,检查标准管理构件接口和应用设计模式的过程)来实现runTest()以创建一个测试实例。这要求测试的名字就是需要调用的测试方法的名字: <o:p></o:p>

TestCase test01 = new testCar("testGetWheels"); <o:p></o:p>

     JUnit会动态查找并调用指定的测试方法。动态的方法很简洁,但如果你键入了错误的名字就会得到一个令人奇怪的NoSuchMethodException异常。动态的方法和静态的方法都很好,你可以按照自己的喜好来选择。(先别着急选择,后面还有一种更酷的方法等着你呢。) <o:p></o:p>

<o:p> </o:p>

TestSuite <o:p></o:p>

一旦你创建了一些测试实例,下一步就是要让他们能一起运行。我们必须定义一个TestSuite。在JUnit中,这就要求你在TestCase类中定义一个静态的suite()方法。suite()方法就像main()方法一样,JUnit用它来执行测试。在suite()方法中,你将测试实例加到一个TestSuite对象中,并返回这个TestSuite对象。一个TestSuite对象可以运行一组测试。TestSuite和TestCase都实现了Test接口(interface),而Test接口定义了运行测试所需的方法。这就允许你用TestCase和TestSuite的组合创建一个TestSuite。这就是为什么我们前面说TestCase,TestSuite以及TestSuite组成了一个composite Pattern的原因。例子如下: <o:p></o:p>

<o:p></o:p>

从JUnit 2.0开始,有一种更简单的动态定义测试实例的方法。你只需将类传递给TestSuite,JUnit会根据测试方法名自动创建相应的测试实例。所以你的测试方法最好取名为testXXX()。例子如下: <o:p></o:p>

  1. public static Test suite()    
  2. {    
  3.      TestSuite suite= new TestSuite();    
  4.      suite.addTest(new testCar("testGetWheels"));    
  5.      suite.addTest(new testCar("testGetSeats"));    
  6.      return suite;    
  7. }   

   

   

<o:p></o:p>

从JUnit的设计我们可看出,JUnit不仅可用于单元测试,也可用于集成测试。关于如何用JUnit进行集成测试请参考相关资料。 <o:p></o:p>

  1. public static Test suite()    
  2. {    
  3.      return new TestSuite(testCar.class);    
  4. }    

    

     为了兼容性的考虑,下面列出使用静态方法的例子: <o:p></o:p>

<o:p></o:p>

<o:p></o:p>

java 代码
  1. public static Test suite()    
  2. {    
  3.     TestSuite suite= new TestSuite();    
  4.     suite.addTest(    
  5.         new testCar("Car.getWheels")    
  6.         {      
  7.             protected void runTest()    
  8.             {    
  9.                 testGetWheels();    
  10.             }    
  11.         }    
  12.     );    
  13.     return suite;   
  14. }   

 

TestRunner <o:p></o:p>

    有了TestSuite我们就可以运行这些测试了,JUnit提供了三种界面来运行测试 <o:p></o:p>

[Text UI] junit.textui.TestRunner <o:p></o:p>

[AWT UI] junit.awtui.TestRunner <o:p></o:p>

[Swing UI] junit.swingui.TestRunner <o:p></o:p>

     我们前面已经看过文本界面了,下面让我们来看一看图形界面: <o:p></o:p>

     界面很简单,键入类名-testCar。或在启动UI的时候键入类名: <o:p></o:p>

[Windows] D:\>java junit.swingui.TestRunner testCar <o:p></o:p>

[Unix] % java junit.swingui.TestRunner testCar <o:p></o:p>

     从图形UI可以更好的运行测试可查单测试结果。还有一个问题需要注意:如果JUnit报告了测试没有成功,JUnit会区分失败(failures)和错误(errors)。失败是一个期望的被assert方法检查到的结果。而错误则是意外的问题引起的,如ArrayIndexOutOfBoundsException。 <o:p></o:p>

     由于TestRunner十分简单,界面也比较直观,故不多介绍。朋友们可自行参考相关资料。 <o:p></o:p>

<o:p> </o:p>

JUnit最佳实践 <o:p></o:p></

分享到:
评论

相关推荐

    junit实战第二版

    《JUnit实战第二版》是一本深入探讨Java单元测试框架JUnit的专著,旨在帮助开发者熟练掌握和运用JUnit进行高效、可靠的软件测试。JUnit是Java领域中最广泛使用的单元测试框架之一,它为开发者提供了编写和执行可重复...

    junit_4.10.rar

    本篇文章将聚焦于JUnit 4.10这一重要的里程碑版本,探讨它引入的新特性以及如何在实际开发中充分利用这些特性进行高效测试。 一、JUnit简介 JUnit是基于Java的开源测试框架,最初由Ernst Meyer和Kent Beck开发,其...

    JUnit知识点滴集合笔记

    JUnit知识点滴集合笔记,这个包里面包括了好几篇文档,一定会让你对JUNIT有一个全新的认识。 JUnit 4 抢先看.doc JUnit in java 真正的测试用例实战.doc JUnit起步.doc junit实现过程.doc JUnit中如何测试异常.doc ...

    Junit-4.12.zip

    本篇文章将聚焦于JUnit 4.12版本,通过源码解析和实际应用,带你深入理解和掌握这个强大的测试工具。 一、JUnit简介 JUnit是Java开发中的一个开源测试框架,它为编写可重复运行的测试提供了简洁的API。JUnit 4.12是...

    junit4入门

    这篇教程将带你走进Junit4的世界,深入理解其核心概念和使用方法。 1. **基本概念** Junit4的核心概念包括测试类、测试方法、断言和注解。测试类通常与待测试的生产代码相独立,其中包含多个测试方法。测试方法是...

    JUnit -- 分析

    10. **实战示例**:结合实际项目,展示如何利用JUnit进行单元测试,解决实际问题。 而"junit.uml"这个文件名称,很可能包含了一张关于JUnit类图或组件图的统一建模语言(UML)表示,这有助于理解JUnit的架构和组件...

    junit总结

    三、JUnit实战应用 1. 单元测试:通过JUnit,开发者可以对单个方法或小块代码进行测试,确保其在各种情况下都能正常工作。这有助于早期发现和修复问题,提高代码质量。 2. 回归测试:当代码发生修改后,可以重新...

    JUnit入门笔记~

    这篇入门笔记将带你深入了解JUnit的基本概念、使用方法以及其在实际开发中的应用。 **一、JUnit简介** JUnit是一个开源的、基于Java的测试框架,它简化了对代码进行单元测试的过程。通过JUnit,开发者可以快速地...

    junit4-r4.12

    本篇文章将深入探讨Junit4-r4.12的核心概念、功能以及实际应用。 1. **单元测试**: 单元测试是对软件中的最小可测试单元进行检查和验证的过程。在Java中,这些单元通常是个体的方法或类。Junit4-r4.12 提供了简洁...

    junit-4.8.2.rar

    本篇文章将深入探讨JUnit 4.8.2的核心特性、使用方法以及它在软件开发中的重要性。 一、JUnit概述 JUnit是一个开源的测试框架,由Ernst Klemens和Kent Beck在2000年推出,基于xUnit测试框架模型。它的设计目标是...

    Junit测试demo

    本篇将深入探讨"Junit测试demo",旨在帮助您理解如何利用JUnit进行有效的单元测试。 一、JUnit简介 JUnit是XUnit家族的一员,由Ernst von Brühl和Kent Beck在2000年创建。它提供了断言机制、测试注解、测试套件和...

    Spring Web Service 实战篇(1)

    《Spring Web Service实战篇(1)》主要涵盖了在Java环境中使用Spring框架构建Web服务的相关技术。本实战篇将深入探讨Spring Web Service的核心概念、架构设计以及实现步骤,旨在帮助开发者熟练掌握这一强大的工具。 ...

    [原]深入JUnit4.x

    这篇深入的文章将围绕JUnit4.x的核心概念、功能以及如何有效地利用它进行测试展开讨论。 1. **注解驱动的测试**: JUnit4.x弃用了JUnit3.x中的`setUp()`和`tearDown()`方法,转而采用注解的方式进行测试前后的准备...

    9.Jenkins持续集成插件实战篇

    《Jenkins持续集成插件实战篇》主要涵盖了 Jenkins 在软件开发过程中的持续集成实践,它是一种强大的自动化工具,尤其在持续集成和持续部署(CI/CD)领域具有广泛的应用。以下将详细介绍 Jenkins 及其在实际工作中的...

    junit.rar_Java编程_Java_

    "JUnit实战篇.mht"和"JUnit实战篇1.mht"很可能是两篇深入的实战文章,涵盖了在真实项目中如何运用JUnit进行测试驱动开发(TDD)或者行为驱动开发(BDD),可能包括了如何编写集成测试、如何进行测试隔离、以及如何...

    J2EE复习积累(三)-JUnit4概述(三)

    本篇文章将深入探讨JUnit4的核心概念、功能和使用方法。 **一、JUnit4简介** JUnit4是对JUnit3的重大升级,引入了注解(Annotations)来简化测试代码,使得测试类更加简洁。此外,JUnit4允许用户自定义测试规则,...

    maven 实战 所有源代码

    本篇文章将深入探讨"Maven实战"中涉及的所有源代码,帮助开发者更深入地理解和运用Maven。 ### 一、Maven的项目结构 在Maven中,项目的标准目录结构(也称为"Maven目录布局")至关重要。源代码文件通常分布在以下...

    简单的银行转帐系统(含JUNIT测试)

    【描述】提及的博文链接指向了ITEYE博客上的一篇文章,该文章详细介绍了该项目的实现过程。博主可能分享了代码结构、主要类的功能、以及如何利用JUNIT进行单元测试以确保代码的正确性。通常,这样的系统会包括账户类...

    Maven实战(入门篇)

    ### Maven实战(入门篇):全面解析Maven的构建、依赖与项目信息管理 #### Maven:构建、依赖与信息管理的全能工具 Maven,源自于希伯来语中的“Baal ha-ne'emânîm”,意指知识的积累、专家或是内行,是一个由...

Global site tag (gtag.js) - Google Analytics