其实我是一个程序员

在woyo写的Junit4培训讲义

 

 

1.OverView

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

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

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

2.概念介绍

2.1简单测试

在附件中的代码包中有生产代码Money类。请看以下对Money类的测试代码(所有测试代码也可以在附件的代码包中查找)

import static org.junit.Assert.assertTrue;


import org.junit.Test;


public class TestSample {


         @Test

         public voidadd() {

                   Moneym12CHF = new Money(12, "CHF");

                   Moneym14CHF = new Money(14, "CHF");

                   Moneyexpected = new Money(26, "CHF");

                   Moneyresult = m12CHF.add(m14CHF);

                   assertTrue(expected.equals(result));

         }

}

在这段测试代码中有几点需要注意和Junit3测试代码不同之处:

1该测试方法主要测试Money类中add()方法是否能正确对货币进行操作。在Junit4中对测试方法只要用@Test就标明该方法是测试方法。方法名字可以自行定义,不像Junit3中所有测试方法都要有test前缀。

2测试类也不需要继承TestCase。Junit4中测试类不需要继承任何Junit中接口和类。

3断言在Junit4中要静态导入。如本段代码中的assertTrue

           Junit4特性测试

请先阅读一遍下列代码,然后我会对这段代码的一些Junit4特性进行详细说明。

import static org.junit.Assert.assertTrue;


import java.util.ArrayList;


import org.junit.After;

import org.junit.Before;

import org.junit.Ignore;

import org.junit.Test;


public class TestFixture {

         private Money f12CHF;

         private Money f14CHF;

         private Money f26CHF;

         private Money f28USD;


         @Before

         public voidsetUp() {

                   f12CHF = newMoney(12, "CHF");

                   f14CHF = newMoney(14, "CHF");

                   f26CHF = newMoney(26, "CHF");

                   f28USD = newMoney(28, "USD");

         }


         @Test

         public voidtestAdd() {

                   Moneyresult = f12CHF.add(f14CHF);

                   assertTrue(f26CHF.equals(result));

         }


         @Ignore("thistest method isn't working now")

         @Test(expected = IndexOutOfBoundsException.class)

         public voidempty() {

                   new ArrayList<Object>().get(0);

         }


         @After

         public voidtearDown() {

                   f12CHF = null;

                   f14CHF = null;

                   f26CHF = null;

                   f28USD = null;

         }


         @Test(timeout = 1000)

         public voidtestTimeOut() throwsInterruptedException {

                   //                 wait(100);

                   Moneyresult = f12CHF.add(f14CHF);

         }

}

1.@Before和@After

在Junit3中对测试数据的初始化是放在setUp方法中,而对测试数据的重置和销毁是放在tearDown方法中。在Junit4中则可以使用@Before和@After来代替。在这段代码中方法名字还是使用setUp和tearDown是为了让有Junit3经验的开发者更好理解@Before和@After。其实在Junit4中这两个方法名可以自行定义为其他任何名字。

2.@Test(expected=XXXException.class)

在JUnit4.0之前,对错误的测试,我们只能通过fail来产生一个错误,并在try块里面 assertTrue(true)来测试。现在,通过@Test中的expected属性就可以实现了。expected属性的值是一个异常的类型,如代码中的IndexOutOfBoundsException.class。(注释掉@lgnore(“this test method isn't workingnow”),运行后可查看)

3.@Ignore

有该标记的测试方法在测试中会被忽略。,你可以为该标签传递一个String的参数,来表明为什么会忽略这个测试方法。比如:@lgnore(“this test method isn't workingnow”),在执行的时候,仅会报告该方法没有实现,而不会运行该测试方法。如代码中的empty()方法。

4.@ Test(timeout =…)

该标记传入了一个时间(毫秒)给测试方法,如果测试方法在指定的时间之内没有运行完,则测试会失败,即使被测试方法测试正确也会失败。该标记主要用于测试被测试方法运行所需时间,即用于方法的简单性能测试。(将wait(100);注释后运行查看结果,再取消注释运行查看结果,对比两种结果

再看下列代码。

import static org.junit.Assert.assertTrue;


import org.junit.AfterClass;

import org.junit.BeforeClass;

import org.junit.Test;


public class TestOnce {

         private staticMoney f12CHF;

         private staticMoney f14CHF;

         private staticMoney f26CHF;

         private staticMoney f28USD;


         @BeforeClass

         public staticvoid setUp() {

                   f12CHF = newMoney(12, "CHF");

                   f14CHF = newMoney(14, "CHF");

                   f26CHF = newMoney(26, "CHF");

                   f28USD = newMoney(28, "USD");

         }


         @Test

         public voidtestAdd() {

                   Moneyresult = f12CHF.add(f14CHF);

                   assertTrue(f26CHF.equals(result));

         }


         @AfterClass

         public staticvoid TearDown() {

                   f12CHF = null;

                   f14CHF = null;

                   f26CHF = null;

                   f28USD = null;

         }

}

5.@BeforeClass和@AfterClass

这里@BeforeClass和@AfterClass是为了在一个Test类的所有测试方法执行前后各执行一次。这是为了能在@BeforeClass中初始化一些昂贵的资源,例如数据库连接,然后执行所有的测试方法,最后在@AfterClass中释放资源。

正如你看到的,由于@BeforeClass和@AfterClass仅执行一次,因此它们只能标记静态方法,在所有测试方法中共享的资源也必须是静态引用。可仔细查看上述代码中对私有成员变量和标记@BeforeClass和@AfterClass的方法的类型。

           Junit4参数测试

有时候要对某一特定方法传入各种不同的参数值,用来测试这个方法的健壮性。在Junit3中必须为每种参数值单独写一个独立的测试方法。这样就造成很多测试代码测试的都是同一方法,只是传入的参数值有不同。在Junit4中只需要一个测试方法就能针对N种不同参数值进行N次测试。试看如下代码:

import static org.junit.Assert.assertTrue;


import java.util.Arrays;

import java.util.Collection;


import org.junit.BeforeClass;

import org.junit.Test;

import org.junit.runner.RunWith;

import org.junit.runners.Parameterized;

import org.junit.runners.Parameterized.Parameters;


/**

 * @authorAdministrator

 * 第一个注意点

 */

@RunWith(Parameterized.class)

public class TestParameter {

         private staticMoney f12CHF;

         //       第二个注意点

         private Money expected;

         private Money target;


         @BeforeClass

         public staticvoid setUp() {

                   f12CHF = newMoney(12, "CHF");

         }


         /**

          *  第三个注意点

          * @return

          */

         @Parameters

         public staticCollection words() {

                   return Arrays.asList(new Object[][] { { newMoney(23, "CHF"), new Money(11, "CHF")},

                                     {new Money(28, "CHF"), newMoney(16, "CHF") }


                   });

         }


         /**

          * 第四个注意点

         *参数化测试必须的构造函数

         *@paramexpected   期望的测试结果,对应参数集中的第一个参数

         *@paramtarget 测试数据,对应参数集中的第二个参数

         */

         public TestParameter(Money expected, Moneytarget) {

                   this.expected= expected;

                   this.target= target;

         }


         /**

           *实际需要测试的方法

           */

         @Test

         public voidadd() {

                   assertTrue(expected.equals(f12CHF.add(target)));

         }

}

请详细查看注释中所标注的注意点。首先测试类需要声明@RunWith(Parameterized.class)。然后设置两个成员变量,一个是测试期望返回的值变量,如代码中expected, 还有一个是测试方法传入的参数值变量,如代码中target。针对这两个成员变量,新建包含这两个成员变量的测试类构造方法。再新建一个方法进行参数初始化。必须将该方法声明为 static 并返回一个 Collection 类型。需要用 @Parameters 注解来修饰该方法。在该方法内部,仅仅创建一个多维Object 数组,并将该数组转换为List。然后运行Junit后,测试通过后的界面如下图:

由图可知执行该测试类时,通过 add () 测试方法运行两次,将代码中 words方法里的每个值对运行一次。

           Junit4套件测试

相比Junit3的套件测试,Junit4的套件测试只需要两个注解就可以运行了,而不需要写任何代码。代码示例如下:

import junit.framework.JUnit4TestAdapter;

import junit.framework.Test;


import org.junit.runner.RunWith;

import org.junit.runners.Suite;

import org.junit.runners.Suite.SuiteClasses;



@RunWith(Suite.class)

@SuiteClasses( { TestSample.class,TestFixture.class, TestOnce.class, TestParameter.class })

public class TestSuite {

         public staticTest suite() {

                   return newJUnit4TestAdapter(TestSuite.class);

         }


}

在 JUnit 4中,套件被两个新注解所替代。第一个是 @RunWith,设计它是为了方便让不同的运行器执行一个特别的测试类。 JUnit4绑定一个叫做Suite的套件运行器,必须在@RunWith 注释中指定这个运行器。不仅如此,还必须提供另一项叫做 @SuiteClasses 的注释,它将一个意欲表示测试套件的类列表作为参数。如代码所示,将需要测试的测试类依次放入SuiteClasses中就可以了。另外如果要使用ant1.7之前的版本,则要像代码中所示对suite方法进行编写。如果是1.7后版本或者是使用maven则删除suite方法就可以了。

posted on 2010-11-10 09:37  frankwoo  阅读(499)  评论(0编辑  收藏  举报

导航