另外要说的是,注解 @Before 是定义在 org.junit.Before 这個类中的,因此使用时需要将其引入我们的代码中。这样做了之后,每次我们运行测试方法时,JUnit 都会先运行 setUp() 方法将 result 的值清零。不过要注意的是,这里不再需要 @Test 注解,因为这并不是壹個 test,只是壹個前置工作。同理,如果“在任何测试执行之后需要进行的收尾工作,我们应该使用 @After 来标注,方法与它类似。由于本例比较简单,不需要用到此功能,所以我们只是简单了给它添加了壹個 tearDown() 方法并在收尾时打印壹句话到控制台,并且使用 @After 来注解这個方法。
使用@BeforeClass 和 @AfterClass 来完成只需要执行壹次的前置工作和后置工作
上面我们提到了两個注解 @Before 和 @After ,我们来看看他们是否适合完成如下功能:有壹個类负责对大文件(超过500 MB)进行读写,他的每壹個方法都是对文件进行操作。换句话说,在调用每壹個方法之前,我们都要打开壹個大文件并读入文件内容,这绝对是壹個非常耗费时的操作。如果我们使用 @Before 和 @After ,那么每次测试都要读取壹次文件,效率及其低下。所以我们希望的是,在所有测试壹开始读壹次文件,所有测试结束之后释放文件,而不是每次测试都读文件。JUnit的作者显然也考虑到了这个问题,它给出了@BeforeClass 和 @AfterClass 两個注解来帮我们实现这个功能。从名字上就可以看出,用这两個注解标注的函数,只在测试用例初始化时执行 @BeforeClass 方法,当所有测试执行完毕之后,执行 @AfterClass 进行收尾工作。在这里要注意壹下,每個测试类只能有壹個方法被标注为 @BeforeClass 或 @AfterClass,而且该方法必须是 public static 类型的。
使用@Test 的属性 timeout 来完成限时测试,以检测代码中的死循环
现在假设我们的 Calculator 类中的 square() 方法是個死循环,那应该怎么办呢,比如说像下面这样:
public void square(int n){ for(;;){} } |
如果测试的时候遇到死循环,你的脸上绝对不会露出笑容的。因此,对于那些逻辑很复杂,循环嵌套比较深的、有可能出现死循环的程序,因此壹定要采取壹些预防措施。限时测试是壹個很好的解决方案。我们给这些测试函数设定壹個预期的执行时间,超过了这壹时间,他们就会被系统强行终止,并且系统还会向你汇报该函数结束的原因是因为超时,这样你就可以发现这些 Bug 了。要实现这壹功能,只需要给 @Test 标注加壹個参数timeout即可,代码如下:
@Test(timeout=2000L) public void testSquare() { calculator.square(3); assertEquals(9, calculator.getReuslt()); } |
timeout参数表明了你预计该方法运行的时长,单位为毫秒,因此2000就代表2秒。现在我们让这個测试方法运行壹下,看看失败时是什么效果。
使用@Test 的属性expected来监控测试方法中可能会抛出的某些异常
JAVA中的异常处理也是壹個重点,因此你经常会编写壹些需要抛出异常的函数。如果你觉得壹個函数应该抛出异常,但是它没抛出,这算不算 Bug 呢?这当然是Bug,JUnit 也考虑到了这壹点,并且可以帮助我们找到这种 Bug。例如,我们写的计算器类有除法功能,如果除数是壹個0,那么必然要抛出“除0异常”。因此,我们很有必要对这些进行测试。代码如下:
@Test(expected=java.lang.ArithmeticException.class) public void testDivide(){ calculator.divide(0); } |
如上述代码所示,我们需要使用@Test注解中的expected属性,将我们要检验的异常(这里是 java.lang.ArithmeticException)传递给他,这样 JUnit 框架就能自动帮我们检测是否抛出了我们指定的异常。
指定 JUnit 运行测试用例时的 Runner
大家有没有想过这個问题,当你把测试代码提交给JUnit框架后,框架是如何来运行你的代码的呢?答案就是Runner。在JUnit中有很多个Runner,他们负责调用你的测试代码,每壹個Runner都有其各自的特殊功能,你要根据需要选择不同的Runner来运行你的测试代码。可能你会觉得奇怪,前面我们写了那么多测试,并没有明确指定壹個Runner啊?这是因为JUnit中有壹個默认的Runner,如果你没有指定,那么系统会自动使用默认Runner来运行你的代码。换句话说,下面两段代码含义是完全壹样的:
import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public class CalculatorTest { ...//省略此处代码 } //用了系统默认的JUnit4.class,运行效果完全壹样 public class CalculatorTest { ...//省略此处代码 } |