Junit4实例图解
前言:JUnit元数据
@Before:
使用了该元数据的方法在每个测试方法执行之前都要执行一次。
@After:
使用了该元数据的方法在每个测试方法执行之后要执行一次。
注意:@Before和@After标示的方法只能各有一个。这个相当于取代了JUnit以前版本中的setUp和tearDown方法,当然你还可以继续叫这个名字,不过JUnit不会霸道的要求你这么做了。
@Test(expected=*.class)
在JUnit4.0之前,对错误的测试,我们只能通过fail来产生一个错误,并在try块里面assertTrue(true)来测试。现在,通过@Test元数据中的expected属性。expected属性的值是一个异常的类型
@Test(timeout=xxx):
该元数据传入了一个时间(毫秒)给测试方法,
如果测试方法在制定的时间之内没有运行完,则测试也失败。
@ignore:
该元数据标记的测试方法在测试中会被忽略。当测试的方法还没有实现,或者测试的方法已经过时,或者在某种条件下才能测试该方法(比如需要一个数据库联接,而在本地测试的时候,数据库并没有连接),那么使用该标签来标示这个方法。同时,你可以为该标签传递一个String的参数,来表明为什么会忽略这个测试方 法。比如:@lgnore(“该方法还没有实现”),在执行的时候,仅会报告该方法没有实现,而不会运行测试方法。、
一、包含必要地Package
最主要地一个 Package就是org.junit.*,把它包含进来之后,绝大部分功能就有了。还有一句话也非常地重要“import static org.junit.Assert.*;”,我们在测试的时候使用的一系列assertEquals方法就来自这个包。大家注意一下,这是一个静态包含 (static),是JDK5中新增添的一个功能。也就是说,assertEquals是Assert类中的一系列的静态方法
二、测试类的声明
测试类是一个独立的类,没有任何父类。测试类的名字也可以任意命名,没有任何局限性。它与普通类的区别在于它内部的方法的声明
三、创建一个待测试的对象
你要测试哪个类,那么你首先就要创建一个该类的对象。
private staticCalculator calculator =newCalculator();
为了测试Calculator类,我们必须创建一个calculator对象。
四、测试方法的声明
在测试类中,并不是每一个方法都是用于测试的,你必须使用“标注”来明确表明哪些是测试方法。“标注”也是JDK5的一个新特性,用在此处非常恰当。我们可以看到,在某些方法的前有@Before、@Test、@Ignore等字样,这些就是标注,以一个“@”作为开头。这些标注都是JUnit4自定义 的,熟练掌握这些标注的含义非常重要。
六、 忽略测试某些尚未完成的方法
七、 Fixture(暂且翻译为“固定代码段”)
Fixture 的含义就是“在某些阶段必然被调用的代码”。“在任何一个测试执行之前必须执行的代码”就是一个Fixture,我们用@Before来标注它
一、 高级Fixture
两个Fixture标注,分别是@Before和@After,是否适合完成如下功能:有一个类是负责对大文件(超过 500兆)进行读写,他的每一个方法都是对文件进行操作。换句话说,在调用每一个方法之前,我们都要打开一个大文件并读入文件内容,这绝对是一个非常耗费时间的操作。如果我们使用@Before和@After,那么每次测试都要读取一次文件,效率及其低下。这里我们所希望的是在所有测试一开始读一次文件, 所有测试结束之后释放文件,而不是每次测试都读文件。JUnit的作者显然也考虑到了这个问题,它给出了@BeforeClass 和 @AfterClass两个Fixture来帮我们实现这个功能。从名字上就可以看出,用这两个Fixture标注的函数,只在测试用例初始化时执行@BeforeClass方法,当所有测试执行完毕之后,执行@AfterClass进行收尾工作。在这里要注意一下,每个测试类只能有一个方法被标注为 @BeforeClass或@AfterClass,并且该方法必须是Public和Static的。
二、 限时测试
那个求平方根的函数有Bug,是个死循环:
public voidsquareRoot(intn) ...{
for(; ;) ;//Bug : 死循环
}
如果测试的时候遇到死循环,对于那些逻辑很复杂,循环嵌套比较深的程序,很有可能出现死循环,因此一定要采取一些预防措施。我们给这些测试函数设定一个执行时间,超过了这个时间,他们就会被系统强行终止,并且系统还会向你汇报该函数结束的原因是因为超时,这样你就可以发现这些Bug了。只需要给@Test标注加一个参数即可,代码如下:
@Test(timeout = 1000)
public voidsquareRoot() ...{
calculator.squareRoot(4);
assertEquals(2,calculator.getResult());
}
Timeout参数表明了你要设定的时间,单位为毫秒,因此1000就代表1秒。
三、 测试异常
经常会编写一些需要抛出异常的函数,如果一个函数应该抛出异常,但是它没抛出,当然是Bug。例如,我们写的计算器类有除法功能,如果除数是一个0,那么必然要抛出“除0异常”。因此,我们很有必要对这些进行测试。代码如下:
@Test(expected = ArithmeticException.class)
public void divideByZero() ...{
calculator.divide(0);
}
如上述代码所示,我们需要使用@Test标注的expected属性,将我们要检验的异常传递给他,这样JUnit框架就能自动帮我们检测是否抛出了我们指定的异常。
四、 Runner (运行器)
把测试代码提交给JUnit框架后,框架如何来运行代码呢?答案就是——Runner。在JUnit中有很多个 Runner,他们负责调用测试代码,每一个Runner都有各自的特殊功能,要根据需要选择不同的Runner来运行测试代码。JUnit中有一个默认Runner,如果没有指定,那么系统自动使用默认 Runner来运行你的代码。换句话说,下面两段代码含义是完全一样的:
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
@RunWith(Suite.class)
@Suite.SuiteClasses({
MyTestCase.class, //测试类
PartSuite.class, //另一个测试套
})
public class AllTestCases {
}
要想指定一个Runner,需要使用@RunWith标注,并且把你所指定的Runner作为参数传递给它。另外一个要注意的 是,@RunWith是用来修饰类的,而不是用来修饰函数的。只要对一个类指定了Runner,那么这个类中的所有函数都被这个Runner来调用。
五、 参数化测试
一个对考试分数进行评价的函数,返回值分别为“优秀,良好,一般,及格,不及格”,因此你在编写测试的时候,至少要写5个测试,把这5中情况都包含了,这 确实是一件很麻烦的事情。我们还使用我们先前的例子,测试一下“计算一个数的平方”这个函数,暂且分三类:正数、0、负数。测试代码如下:
importorg.junit.AfterClass;
importorg.junit.Before;
importorg.junit.BeforeClass;
importorg.junit.Test;
importstatic org.junit.Assert.*;
public classAdvancedTest ...{
private static Calculator calculator =new Calculator();
@Before
public void clearCalculator() ...{
calculator.clear();
}
@Test
public void square1() ...{
calculator.square(2);
assertEquals(4,calculator.getResult());
}
@Test
public void square2() ...{
calculator.square(0);
assertEquals(0, calculator.getResult());
}
@Test
public void square3() ...{
calculator.square(-3);
assertEquals(9,calculator.getResult());
}
}
为了简化类似的测试,JUnit4提出了“参数化测试”的概念,只写一个测试函数,把这若干种情况作为参数传递进去,一次性的完成测试。代码如下:
importstatic org.junit.Assert.assertEquals;
importorg.junit.Test;
importorg.junit.runner.RunWith;
importorg.junit.runners.Parameterized;
importorg.junit.runners.Parameterized.Parameters;
importjava.util.Arrays;
importjava.util.Collection;
@RunWith(Parameterized.class)
public classSquareTest{
private static Calculator calculator = new Calculator();
private int param;
private int result;
@Parameters
public static Collection data(){
return Arrays.asList(newObject[][]...{
{2, 4},
{0, 0},
{-3, 9},
});
}
//构造函数,对变量进行初始化
六、断言和假设
断言:org.junit.Assert用于测试用例中,如果断言失败,用例即结束。
假设:org.junit.Assume用于在准备环境时判断环境是否符合要求,包括测试套的@BeforeClass,测试类的@BeforeClass,测试类的实例化,测试类的@Before。
如果假设失败,假设所处初始化代码方法立即结束,更深级别的后续工作也被忽略,相关测试用例被忽略,但与假设同级别的收尾工作还要继续执行。
例如:如果在测试类的@BeforeClass中假设失败,该类的实例化及子级别将被忽略,@AfterClass会继续执行。