JUnit单元测试--小试牛刀

单元测试更多的是在开发阶段完成,开发人员每写一个函数的时候都会写相应的单元测试。对于java代码,广泛使用的是jUnit,根据jUnit能够本身相应的开发一套自动化测试框架。这个的前提是要学会junit,先知道怎么用,才能知道怎么为我所用。java

学习JUnit的操做很简单,JUnit是一个敏捷编程的开发框架,他的设计很值得学习。这也是我学习JUnit的缘由。sql

JUnit最大的特色就是:各个方法之间是独立的,一个方法的失败不会影响另外一个方法的执行。数据库

JUnit 3:是基于反射机制的方法,有命名的约束,必须以test开头。每个类都继承了TestCase类,而TestCase的父类是Assert类。即全部测试类都是TestCase类的子类。编程

JUnit 4:引入了注解(annotation),经过解析注解就能够为测试提供相应的信息。@Test代表这是要执行的测试方法,无论测试方法的名字是什么,都会执行。每个类能够不继承任何类,能够是一个普通类也能够去继承一个类或实现一个接口,要实现测试只须要在要测试的方法以前加@Test注释就能够了,可是仍然能够直接使用断言,静态导入import static org.junit.Assert.*;数组

为了更好地理解junit测试用例,我找了一个简单的例子来练习:框架

例若有这么一个类Greeting,根据天天不一样的时间来返回不一样的问候语:函数

package com.test.one;
 
public class Greeting {
    public static final String GREETING_MORNING = "Good Morning Sunshine!";
    public static final String GREETING_AFTERNOON = "Just a few more hours before quiting time!";
    public static final String GREETING_EVENING = "I am outta here";
    public static final int MAX_HOUR_MORNING = 12;
    public static final int MAX_HOUR_AFTERNOON = 17;
 
    private java.sql.Timestamp iGreetingTime = null;
 
    public Greeting(){
        this(System.currentTimeMillis());
    }
    public Greeting(long greetingTime) {
        super();
        iGreetingTime = new java.sql.Timestamp(greetingTime);
    }
    public String getGreeting(){
 
        if(iGreetingTime.getHours()<MAX_HOUR_MORNING){
            return GREETING_MORNING;
        }else if (iGreetingTime.getHours()<MAX_HOUR_AFTERNOON) {
            return GREETING_AFTERNOON;
        }else {
            return GREETING_EVENING;
        }
 
    }
 
}

  

咱们须要测试这个类,看是否能够根据时间的不一样,来呈现不一样的问候语。建立junit的测试用例须要遵循一下几点:单元测试

一、Junit类需继承TestCase学习

二、setUp()和setDown()为每一个方法准备或销毁测试装备测试

三、建立public的方法,方法名为test开头,如testMorningGreeting(),使用assert判断实际返回的值和指望值

四、super(testMethod)为每一个测试方法生成实例。首先执行setUp(),而后执行testMethod ,最后执行tearDown()。

package com.test.test;
 
import java.sql.Time;
 
import com.test.one.Greeting;
 
import junit.framework.TestCase;
 
public class GreetingTest extends TestCase {
 
    protected void setUp() throws Exception {
        super.setUp();
    }
 
    protected void tearDown() throws Exception{
        super.tearDown();
    }
 
    public void testMorningGreeting() throws Exception {
        Time time = new Time(9, 0, 0);
        Greeting greeting  = new Greeting(time.getTime());
        assertEquals("the morning greeting expected", Greeting.GREETING_MORNING, greeting.getGreeting());
    }
    public void testAfternoonGreeting() throws Exception {
        Time time = new Time(12, 0, 0);
        Greeting greeting  = new Greeting(time.getTime());
        assertEquals("the Afternoon greeting expected", Greeting.GREETING_AFTERNOON, greeting.getGreeting());
    }
    public void testEveningGreeting() throws Exception {
        Time time = new Time(18, 0, 0);
        Greeting greeting  = new Greeting(time.getTime());
        assertEquals("the Evening greeting expected", Greeting.GREETING_EVENING, greeting.getGreeting());
    }
 
    public GreetingTest(String testMethod){
        super(testMethod);
    }
}

 

这个只是单个测试用例,junit也提供了测试套件组织想要执行的测试用例。

若是你已经有了两个测试用例,AddJobCmdImpTest、RemoveJobCmdImpTest等TestCase的子类别,若是想一次运行这两个测试,须要使用AllTest的类,表明一个测试套件。

public class AllTest {
    public static Test suite(){
        TestSuite suite = new TestSuite("Test for XXXX");
        suite.addTest(new TestSuite(AddJobCmdImpTest.class));
        suite.addTest(new TestSuite(RemoveJobCmdImpTest.class));
        return suite;
    }
}

如下是JUnit4的代码:

public class GreetingTest extends TestCase {
  
    @Before
    public void setUp() throws Exception {
        super.setUp();
         
    }
    @After
    public void tearDown() throws Exception{
        super.tearDown();
    }
     @Test
    public void ttrestMorningGreeting() throws Exception {
        Time time = new Time(9, 0, 0);
        Greeting greeting  = new Greeting(time.getTime());
        assertEquals("the morning greeting expected", Greeting.GREETING_MORNING, greeting.getGreeting());
    }
     @Test
    public void testAfternoonGreeting() throws Exception {
        Time time = new Time(12, 0, 0);
        Greeting greeting  = new Greeting(time.getTime());
        assertEquals("the Afternoon greeting expected", Greeting.GREETING_AFTERNOON, greeting.getGreeting());
    }
     @Test
    public void testEveningGreeting() throws Exception {
        Time time = new Time(18, 0, 0);
        Greeting greeting  = new Greeting(time.getTime());
        assertEquals("the Evening greeting expected", Greeting.GREETING_EVENING, greeting.getGreeting());
    }
}
 

 

关于JUnit4的一些知识点:

1)、注释

 注释  说明
@Before

用于方法注释,表示该方法在每一个测试方法执行前执行一次,可用于一些初始工做

@BeforeClass 用于方法注释,该方法在全部测试方法运行前运行,且只运行一次,添加该注释的方法必须修饰为 public static void 且没有参数。
@Test

方法注释,表示测试方法。该方法有两个属性 

a: expected :该属性表示测试方法必须抛出一个异常,且异常的类型必须是该属性要求的类型,不然表示测试方法失败。也叫作异常测试。 例如:@Test(expected=IndexOutOfBoundsException.class) 

b:timeout 用于超时测试,表示该测试方法的执行时间若是超过了要求的时间则失败 单位为毫秒 

例如:@Test(timeout=100)

@Ignore 方法注释,表示会被忽略的测试方法
@After

方法注释,被注释的方法会在每一个测试方法执行完成以后执行一次,若是其它的方法抛出了异常,该方法一样会被执行。主要用于释放在@Before方法中初始化的资源。

@AfterClass

方法注释,功能同@After,只不过是该方法释放的是@BeforeClass方法 

初始化的资源。且在全部的测试方法执行完成以后,只执行一次。

一个JUnit 4 的单元测试用例执行顺序为: 

@BeforeClass –> @Before –> @Test –> @After –> @AfterClass 

2)、failure 和error

      failure:是因为预期的结果和实际运行结果不一样而致使的,例如当使用assertEqual()或其它assertXXX()方法断言失败时,就会报出failure,若是发现failure,你就要去检查你的测试方法或者是被测试方法中的填写的逻辑是否有误。简单点,就是预想到的。

      error:是填写程序是没有考虑到的问题,在执行测试的断言以前,程序就由于某种类型的意外而中止,好比说咱们在操做数组的时候,由于存取超出索引就会引起ArrayIndexOutOfBoundsException,这时程序就会报error,程序将没法运行下去,提早结束,这个时候就要检查被测试方法中是否由欠缺考虑的地方。简单点,就是预想不到的,没有执行到断言。

JUnit4是JUnit框架有史以来的最大改进,其主要目标即是利用Java5的Annotation特性简化测试用例的编写。Annotation翻译成元数据。元数据是什么?元数据就是描述数据的数据。也就是说,这个东西在Java里面能够用来和public、static等关键字同样来修饰类名、方法名、变量名。修饰的做用描述这个数据是作什么用的,差很少和public描述这个数据是公有的同样。

@Test(expected=*.class)
在JUnit4.0以前,对错误的测试,咱们只能经过fail来产生一个错误,并在try块里面assertTrue(true)来测试。如今,经过@Test元数据中的expected属性。expected属性的值是一个异常的类型


@Test(timeout=xxx):
该元数据传入了一个时间(毫秒)给测试方法,
若是测试方法在制定的时间以内没有运行完,则测试也失败。

@ignore:
该元数据标记的测试方法在测试中会被忽略。当测试的方法尚未实现,或者测试的方法已通过时,或者在某种条件下才能测试该方法(好比须要一个数据库联接,而在本地测试的时候,数据库并无链接),那么使用该标签来标示这个方法。同时,你能够为该标签传递一个String的参数,来代表为何会忽略这个测试方法。好比:@lgnore(“该方法尚未实现”),在执行的时候,仅会报告该方法没有实现,而不会运行测试方法。

@RunWith(Parameterized.class) 参数化测试。

你可能遇到过这样的函数,它的参数有许多特殊值,或者说他的参数分为不少个区域。好比,一个对考试分数进行评价的函数,返回值分别为“优秀,良好,通常,及格,不及格”,所以你在编写测试的时候,至少要写5个测试,把这5中状况都包含了,这确实是一件很麻烦的事情。只写一个测试函数,把这若干种状况做为参数传递进去,一次性的完成测试。

@RunWith(Parameterized.class)

public class SquareTest ...{

    private static Calculator calculator = new Calculator();

  private int param;

    private int result;    

@Parameters   

public static Collection data() ...{

        return Arrays.asList(new Object[][]...{

                ...{2, 4},

                ...{0, 0},

                ...{-3, 9},

        });

}

//构造函数,对变量进行初始化

public SquareTest(int param, int result) ...{

        this.param = param;
        this.result = result;

}

@Test   

public void square() ...{

        calculator.square(param);

        assertEquals(result, calculator.getResult());

    }

 }

下面咱们对上述代码进行分析。首先,你要为这种测试专门生成一个新的类,而不能与其余测试共用同一个类,此例中咱们定义了一个SquareTest类。而后,你要为这个类指定一个Runner,而不能使用默认的Runner了,由于特殊的功能要用特殊的Runner嘛。@RunWith(Parameterized.class)这条语句就是为这个类指定了一个ParameterizedRunner。第二步,定义一个待测试的类,而且定义两个变量,一个用于存放参数,一个用于存放期待的结果。接下来,定义测试数据的集合,也就是上述的data()方法,该方法能够任意命名,可是必须使用@Parameters标注进行修饰。这个方法的框架就不予解释了,你们只须要注意其中的数据,是一个二维数组,数据两两一组,每组中的这两个数据,一个是参数,一个是你预期的结果。好比咱们的第一组{2, 4},2就是参数,4就是预期的结果。这两个数据的顺序无所谓,谁前谁后均可以。以后是构造函数,其功能就是对先前定义的两个参数进行初始化。在这里你可要注意一下参数的顺序了,要和上面的数据集合的顺序保持一致。若是前面的顺序是{参数,期待的结果},那么你构造函数的顺序也要是“构造函数(参数, 期待的结果)”,反之亦然。

打包测试

在一个项目中,只写一个测试类是不可能的,咱们会写出不少不少个测试类。但是这些测试类必须一个一个的执行,也是比较麻烦的事情。鉴于此,JUnit提供了打包测试的功能,将全部须要运行的测试类集中起来,一次性的运行完毕,大大的方便了咱们的测试工做。具体代码以下:

@RunWith(Suite.class)

@Suite.SuiteClasses(...{CalculatorTest.class, SquareTest.class})

public class AllCalculatorTests ...{}

你们能够看到,这个功能也须要使用一个特殊的Runner,所以咱们须要向@RunWith标注传递一个参数Suite.class。同时,咱们还须要另一个标注@Suite.SuiteClasses,来代表这个类是一个打包测试类。咱们把须要打包的类做为参数传递给该标注就能够了。有了这两个标注以后,就已经完整的表达了全部的含义,所以下面的类已经可有可无,随便起一个类名,内容所有为空既可。

相关文章
相关标签/搜索