JUnit 实例讲解

JUnit 4是JUnit框架有史以来的最大改进,其主要目标便是利用Java 5Annotation特性简化测试用例的编写。让我们看看如何使用JUnit 4来进行Unit测试。

请注意:本文主要介绍JUnit 4的最新特性和用法,并不会讲解Unit测试的基础。如果你对JUnit框架还不熟悉,请先参考Eclipse快速上手指南 (2) 使用JUnit测试”一文,学习如何编写JUnit测试。

我们使用的开发环境是Eclipse 3.2,它已经自带了JUnit 4.1,你需要将JUnit 4 Library添加到项目用到的Library中。另外,必须使用JDK 5.0或更高版本。

要在Eclipse环境之外运行JUnit,需要下载JUnit 4.1,具体请访问JUnit.org

 

我们先看一个简单的Math类:

package com.javaeedev.junit4;
public class Math {

    public int abs(int value) {
        return value>=0 ? value : (-value);
    }

    public int div(int a, int b) {
        return a / b;
    }

    /**
     * BUG: if b less than 0!
     */
    public float exp(int a, int b) {
        float r = 1;
        for(int i=0; i<b; i++)
            r = r * a;
        return r;
    }
}

注意exp()方法是有Bug的,如果传入参数2, -1,则期待的返回值应为0.5F,但实际返回值为1.0F

下面我们看看传统的JUnitTestCase

public class MathTest extends TestCase {

    public void setUp() { super.setUp(); }
    public void tearDown() { super.tearDown(); }

    public void testAbs() { assertTrue(true); }
    public void testDiv() {...}
    public void testExp() {...}

}

JUnit依赖反射来执行每个以test开头的方法。然而,在最新的JUnit 4中,由于有了Annotation的支持,我们的测试方法不需要再以testXxx标识了,而是写上一个@Test标注即可。例如:

@Test public void doAbs() {...}

甚至MathTest类也不必继承自TestCase。你也许会想到,不继承自TestCase就无法调用assertXxx方法了,正因为如此, 所有的assertXxx方法全部以静态方法被放入了Assert类,使用Assert.assertXxx()调用。如果使用

import static org.junit.Assert.*;

则原有的代码不必改动。

setUp()和tearDown()方法也依赖@Before@After标记,这样做的最大的好处是在继承体系内不必担心忘记了在 setUp()方法中调用父类的super.setUp()方法,JUnit框架会自动处理父类的@Before@After标记的方法。

并且,JUnit框架对@Before@After的调用顺序类似于类的构造方法和析构方法,即@Before按照父类到子类的顺序调用,@After则相反,这样保证了资源的正确获取和释放。

当然,不再强迫必须使用setUptearDown作为方法名,可以使用更有意义的方法名,例如:initDatabase()closeDatabase(),只要它们被标注了@Before@After即可。

来看看使用AnnotationMathTest

package com.javaeedev.junit4;

import static org.junit.Assert.*;

import org.junit.*;

public class MathTest {

    public MathTest() {
        System.out.println("new MathTest instance.");
    }

    @Before
    public void setUp() throws Exception {
        System.out.println("call @Before before a test method");
    }

    @After
    public void tearDown() throws Exception {
        System.out.println("call @After after a test method");
    }

    @Test
    public void doAbs() {
        Math math = new Math();
        assertEquals(200, math.abs(200));
        assertEquals(100, math.abs(-100));
        assertEquals(0, math.abs(0));
    }

    @Test
    public void doDiv() {
        Math math = new Math();
        assertEquals(5, math.div(100, 20));
        assertEquals(4, math.div(100, 21));
    }

    @Test(expected=ArithmeticException.class)
    public void doDiv0() {
        new Math().div(127, 0);
    }

    @Test(timeout=1)
    public void doLongTimeTask() {
        double d = 0;
        for(int i=1; i<10000000; i++)
            d+=i;
    }

    @Test
    public void testExp() {
        Math math = new Math();
        assertEquals(32f, math.exp(2, 5), 0.001f);
        assertEquals(1f, math.exp(2, 0), 0.001f);
        assertEquals(0.5f, math.exp(2, (-1)), 0.001f);
    }

}

对测试异常,JUnit 4可以用expected=Exception.class来期待一个预期的异常,而不必编写

try {
  ...
  fail("No exception");
}
catch(Exception e) {
  // OK!
}

来看看doDiv0测试,我们期待一个除数为0ArithmeticException,因此编写如下测试方法:

@Test(expected=ArithmeticException.class)
public void doDiv0() {
    new Math().div(127, 0);
}

对于非常耗时的测试,@Test还有一个timeout来标识该方法最长执行时间,超过此时间即表示该测试方法失败:

@Test(timeout=1)
public void doLongTimeTask() {
    double d = 0;
    for(int i=1; i<10000000; i++)
        d+=i;
}

以上方法若执行时间超过1ms则测试失败,由于依赖CPU的执行速度,在不同的机器上测试结果也不同。

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

正如你能想到的,由于@BeforeClass@AfterClass仅执行一次,因此它们只能标记静态方法,在所有测试方法中共享的资源也必须是静态引用:

private static Object dbConnection;

@BeforeClass
public static void setUpBeforeClass() throws Exception {
    System.out.println("call @BeforeClass and init database connection");
    dbConnection = new Object();
}

@AfterClass
public static void tearDownAfterClass() throws Exception {
    System.out.println("call @AfterClass to release database connection");
    dbConnection = null;
}

各个方法执行顺序如下:

call @BeforeClass and init database connection

new MathTest instance.
call @Before before a test method
call @After after a test method

new MathTest instance.
call @Before before a test method
call @After after a test method

...

call @AfterClass to release database connection

可以看到,@BeforeClass是在实例化MathTest之前调用的,因此不能在构造方法中初始化共享资源。

最后需要注意的是由于Java 5的自动Box/Unbox特性,在调用assertEquals()时要特别注意,如果你传入:

assertEquals(100F, 100);

则按照自动Box变为:

assertEquals(new Float(100F), new Integer(100));

测试失败,因为Float类和Integer类不是同一类型。

因此要特别注意floatdouble的测试。事实上对floatdouble应使用

assertEquals(float, float, float delta);
assertEquals(double, double, double delta);

delta指定了两个作比较的浮点数的相差范围,在此范围内的两个浮点数将认为相等。可以传入一个很小的数例如0.0001F 

JUnit 4非常适合使用Java 5的开发人员,但是无法在Java 1.4中获得这些好处,并且,也不与以前的版本兼容。因此,如果你正在使用Java 5,就可以考虑使用JUnit 4来编写测试。

ZIP包下载:javaeedev.googlecode.com/files/JUnit4Example.zip

参考

http://www.javaeedev.com/blog/article.jspx?articleId=ff80808112e766ee011312f144520061

 

 

2. @BeforeClass/@AfterClass 和 @Before/@After的区别

 

@BeforeClass and @AfterClass 

@Before and @After 

在一个类中只可以出现一次

在一个类中可以出现多次,即可以在多个方法的声明前加上这两个Annotaion标签,执行顺序不确定

方法名不做限制

方法名不做限制

在类中只运行一次

在每个测试方法之前或者之后都会运行一次

@BeforeClass父类中标识了该<span style="font-family: Times New Roma

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值