JUnit 4的参数化测试

来源:互联网 发布:网络大型策略游戏 编辑:程序博客网 时间:2024/05/16 06:46

最近在研究TestN与JUnit 4的优劣势。在测试JUnit 4的@Parameters的时候,遇到initializationError的错误。所以,想彻底研究了一下这个问题出现的原因,并找出解决方法。

问题描述:

之前使用JUnit的时候,可以直接以JUnit test形式运行test类中某个用@Test注解的函数。但是如果test类中使用了@RunWith(Parameterized.class)注解,这种运行方法就会出现initializationError的错误。


被测试的类Math:

package com.ibm.junit.parameter;/** * @author yuejming@cn.ibm.com * */public class Math {    public static int divide(int x,int y) {           return x/y;       }         public static int multiple(int x,int y) {           return x*y;       } }

测试的类MathTest:

package com.ibm.junit.parameter;import static org.junit.Assert.assertEquals;import java.util.Arrays;import java.util.Collection;import org.junit.AfterClass;import org.junit.BeforeClass;import org.junit.Test;import org.junit.runner.RunWith;import org.junit.runners.Parameterized;import org.junit.runners.Parameterized.Parameters;/** * @author yuejming@cn.ibm.com * */@RunWith(Parameterized.class)public class MathTest {        int faciend;    int multiplicator;    int result;    @Parameters    public static Collection multipleValues() {        System.out.println("Initialize Data!");     return Arrays.asList(new Object[][] {        {2, 3, 6 },        {3, 4, 12 },        {4, 5, 20 }     });    }        public MathTest(int faciend, int multiplicator, int result) {        System.out.println("Constructor Method with no Parameters!");                this.faciend = faciend;        this.multiplicator = multiplicator;        this.result = result;    }        /*public MathTest() {        System.out.println("Constructor Method with no Parameters!");    }*/    @BeforeClass    public static void setUpBeforeClass() throws Exception {        System.out.println("Before Class!");    }    @AfterClass    public static void tearDownAfterClass() throws Exception {        System.out.println("After Class!");    }    @Test(expected=ArithmeticException.class)    public void testDivide() {        System.out.println("Test Divide");                assertEquals(3,Math.divide(9,3));        assertEquals(3,Math.divide(10,3));        Math.divide(10,0);//除数不能为0,会抛出异常    }    @Test    public void testMultiple() {        System.out.println("Test Multiple");                assertEquals(result,Math.multiple(faciend,multiplicator));    }}


为了分析问题,做了如下对比试验:

1. 直接以JUnit test形式运行MathTest类

    如果运行整个MathTest类,测试通过,输出如下:

Initialize Data!
Before Class!
Constructor Method with no Parameters!
Test Divide
Constructor Method with no Parameters!
Test Multiple
Constructor Method with no Parameters!
Test Divide
Constructor Method with no Parameters!
Test Multiple
Constructor Method with no Parameters!
Test Divide
Constructor Method with no Parameters!
Test Multiple
After Class!

    如果运行MathTest类中的testMultiple()方法,测试通不过,提示initializationError的错误。

2. 注释掉MathTest类前面的@RunWith(Parameterized.class),提示java.lang.Exception: Test class should have exactly one public zero-argument constructor。后面会详细分析注释@RunWith(Parameterized.class)前后的区别。

3. 根据2中提示的错误,注释有参构造函数,提供无参构造函数。不管是对整个MathTest类进行JUnit测试,还是对testMultiple()方法进行JUnit测试,都会通过。

4. 加上MathTest类前面的@RunWith(Parameterized.class),仍然提供无参构造函数,对MathTest类和方法的测试,都通不过,提示java.lang.IllegalArgumentException: 参数数目错误。
 

通过上面的对比试验,下面结合JUnit参数测试的原理,来分析一下原因。

对于JUnit参数化测试,需要在测试类前加上@RunWith(Parameterized.class)。@RunWith用来指定该类使用参数化运行器运行,而不是使用默认情况下的JUnit内建的运行器运行。没有@RunWith注解,就是使用JUnit内建的运行器运行,它需要无参构造函数,可以不自己提供而使用默认无参构造函数。试验3说明了这个问题。虽然测试通过,但是由于没有对参数赋值,参数都是使用的默认值,所以是无效的测试。

使用@RunWith注解,就必须提供有参构造函数。试验4说明了这个问题。并且,每个测试函数运行前,都会调用有参构造函数初始化所有测试数据和验证数据。试验1的输出结果说明这个。

同时,使用@RunWith注解之后,就不能单独对测试类中单个测试函数进行测试。因为单独运行单个测试函数,不会在创建测试类前执行@Parameters注解的函数,不能为参数化测试提供参数,所以会提示initializationError的错误。如果对测试类进行测试,就会在创建测试类前准备好测试数据,然后在执行每个测试函数前通过构造函数为测试函数要用到的测试变量赋值。试验1的输出结果说明这个。