其实我是一个程序员  
日历
<2010年11月>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011
统计
  • 随笔 - 4
  • 文章 - 1
  • 评论 - 3
  • 引用 - 0

导航

常用链接

留言簿

随笔档案

搜索

  •  

最新评论

阅读排行榜

评论排行榜

 

 

1. OverView

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

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

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

2. 概念介绍

2.1 简单测试

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

 1 import static org.junit.Assert.assertTrue ;
 2 import org.junit.Test;
 3 
 4 public class TestSample { 
 5 
 6          @Test
 7          public void add() {
 8                    Money m12CHF = new Money(12"CHF" );
 9 
10                    Money m14CHF = new Money(14"CHF" );
11 
12                    Money expected = new Money(26"CHF" );
13 
14                    Money result = m12CHF.add(m14CHF);
15 
16                    assertTrue (expected.equals(result));
17          }
18 }
19 

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

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

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

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

            Junit4 特性测试

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


 1 import static org.junit.Assert.assertTrue ;
 2 import java.util.ArrayList;
 3 import org.junit.After;
 4 import org.junit.Before;
 5 import org.junit.Ignore;
 6 import org.junit.Test; 
 7 
 8 public class TestFixture {
 9          private Money f12CHF ;
10          private Money f14CHF ;
11          private Money f26CHF ;
12          private Money f28USD ; 
13 
14          @Before
15          public void setUp() {
16                    f12CHF = new Money(12"CHF" );
17                    f14CHF = new Money(14"CHF" );
18                    f26CHF = new Money(26"CHF" );
19                    f28USD = new Money(28"USD" );
20          }
21 
22  
23 
24          @Test
25          public void testAdd() {
26                    Money result = f12CHF .add(f14CHF );
27                    assertTrue (f26CHF .equals(result));
28          }
29 
30  
31          @Ignore ("this test method isn't working now" )
32          @Test (expected = IndexOutOfBoundsException.class )
33          public void empty() {
34                    new ArrayList<Object>().get(0);
35          }
36  
37 
38          @After
39          public void tearDown() {
40                    f12CHF = null ;
41                    f14CHF = null ;
42                    f26CHF = null ;
43                    f28USD = null ;
44          }
45 
46  
47          @Test (timeout = 1000)
48          public void testTimeOut() throws InterruptedException {
49                    //                 wait(100);
50                    Money result = f12CHF .add(f14CHF );
51          }
52 }

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 working now ”) ,运行后可查看

3 @Ignore

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

4. @ Test timeout =…

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

再看下列代码。


 1 import static org.junit.Assert.assertTrue ;
 2 import org.junit.AfterClass;
 3 import org.junit.BeforeClass;
 4 import org.junit.Test;
 5 
 6 public class TestOnce {
 7          private static Money f12CHF ;
 8          private static Money f14CHF ;
 9          private static Money f26CHF ;
10          private static Money f28USD ;
11  
12 
13          @BeforeClass
14          public static void setUp() {
15                    f12CHF = new Money(12"CHF" );
16                    f14CHF = new Money(14"CHF" );
17                    f26CHF = new Money(26"CHF" );
18                    f28USD = new Money(28"USD" );
19          }
20  
21          @Test
22          public void testAdd() {
23                    Money result = f12CHF .add(f14CHF );
24                    assertTrue (f26CHF .equals(result));
25          }
26  
27          @AfterClass
28          public static void TearDown() {
29                    f12CHF = null ;
30                    f14CHF = null ;
31                    f26CHF = null ;
32                    f28USD = null ;
33          }
34 }

5 @BeforeClass @AfterClass

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

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

            Junit4 参数测试

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


 1 import static org.junit.Assert.assertTrue ;
 2 import java.util.Arrays;
 3 import java.util.Collection;
 4 import org.junit.BeforeClass;
 5 import org.junit.Test;
 6 import org.junit.runner.RunWith;
 7 import org.junit.runners.Parameterized;
 8 import org.junit.runners.Parameterized.Parameters;
 9  
10 /**
11   * @author Administrator
12   * 第一个注意点
13   */
14 @RunWith (Parameterized.class )
15 public class TestParameter {
16          private static Money f12CHF ;
17          //       第二个注意点
18          private Money expected ;
19          private Money target ;
20  
21          @BeforeClass
22          public static void setUp() {
23                    f12CHF = new Money(12"CHF" );
24          }
25  
26          /**
27            *  第三个注意点
28            * @return
29            */
30          @Parameters
31          public static Collection words() {
32                    return Arrays.asList (new Object[][] { { new Money(23"CHF" ), new Money(11"CHF" ) },
33                                      { new Money(28"CHF" ), new Money(16"CHF" ) }
34                    });
35          }
36 
37          /**
38            * 第四个注意点
39          * 参数化测试必须的构造函数
40          * @paramexpected    期望的测试结果,对应参数集中的第一个参数
41          * @paramtarget 测试数据,对应参数集中的第二个参数
42          */
43          public TestParameter(Money expected, Money target) {
44                    this .expected = expected;
45                    this .target = target;
46          }
47  
48          /**
49            * 实际需要测试的方法
50            */
51          @Test
52          public void add() {
53                    assertTrue (expected .equals(f12CHF .add(target )));
54          }
55 }

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

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

            Junit4 套件测试

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


 1 import junit.framework.JUnit4TestAdapter;
 2 import junit.framework.Test;
 3 import org.junit.runner.RunWith;
 4 import org.junit.runners.Suite;
 5 import org.junit.runners.Suite.SuiteClasses;
 6 
 7 @RunWith (Suite.class )
 8 @SuiteClasses ( { TestSample.class , TestFixture.class , TestOnce.class , TestParameter.class })
 9 public class TestSuite {
10          public static Test suite() {
11                    return new JUnit4TestAdapter(TestSuite.class );
12          }
13 }

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

posted on 2010-11-10 09:47 吴峻申 阅读(167) 评论(0)  编辑  收藏

只有注册用户登录后才能发表评论。


网站导航:
 
 
Copyright © 吴峻申 Powered by: 博客园 模板提供:沪江博客