全面認識JUnit 4的新特徵

提要 本文將向你介紹如何實現從JUnit 3.8向JUnit 4的遷移。同時,還討論JUnit 4中的一些新特徵,特別包括對註解的使用;最後,簡要介紹這個新版本的IDE集成現狀。

   一、 引言

        在本文開始,我將假定,你已經瞭解由Kent Beck和Erich Gamma發明的這個Java單元測試框架並因此而略過必要的簡介。所以,我將集中分析從JUnit 3.8到最新版本-JUnit 4的遷移過程以及其在IDE和Ant中的集成。

        JUnit 4是一種與其之前的版本完全不同的API,它根據Java 5.0中的新特徵(註解,靜態導入等)構建而成。如你所見,JUnit 4更簡單、更豐富和更易於使用,而且它引入了更爲靈活的初始化和清理工作,還有限時的和參數化測試用例。 

        代碼實例最能說明問題。因此,在本文中,我將使用一個例子來展示不同的測試用例:一個計算器。該示例計算器很簡單,效率並不高,甚至還有一些錯誤;它僅 僅操作整數,並且把結果存儲在一個靜態變量中。Substract方法並不返回一個有效的結果,而且也沒有實現乘法運算,而且看上去在 squareRoot方法中還存在一個錯誤:無限循環。這些錯誤將幫助說明使用JUnit 4進行測試的有效性。你可以打開和關閉這個計算器,而且你可以清除這些結果。下面是其實現代碼:

package calc;
public class Calculator {
  private static int result; //存儲結果的靜態變量
  public void add(int n) {
   result = result + n;
  }
  public void substract(int n) {
   result = result - 1; //錯誤:應該是 "result = result - n"
  }
  public void multiply(int n) {} //還沒實現
  public void divide(int n) {
   result = result / n;
  }
  public void square(int n) {
   result = n * n;
  }
  public void squareRoot(int n) {
   for (; ;) ; //錯誤:無限循環
  }
  public void clear() { //清除結果
   result = 0;
  }
  public void switchOn() { //打開屏幕,顯示 "hello",並報警
   result = 0; //實現其它的計算器功能
  }
  public void switchOff() { } //顯示 "bye bye",報警,並關閉屏幕
  public int getResult() {
   return result;
  }
}

   二、 遷移一個測試類

  現在,我將把一個已經使用JUnit 3.8編寫成的簡單的測試類遷移到JUnit 4。這個類有一些缺陷:它沒有測試所有的業務方法,而且看上去在testDivide方法中還存在一個錯誤(8/2不等於5)。因爲還沒有實現乘法運算功能,所以對其測試將被忽略。

  下面,我們把兩個版本的框架之間的差別以粗體顯示出現於表格1中。

  表格1.分別以JUnit 3.8和JUnit 4實現的CaculatorTest。

   JUnit 3.8

package junit3;
import calc.Calculator;
import junit.framework.TestCase;
public class CalculatorTest extends TestCase {
  private static Calculator calculator = new Calculator();
  @Override protected void setUp() { calculator.clear(); }
  public void testAdd() {
   calculator.add(1);
   calculator.add(1);
   assertEquals(calculator.getResult(), 2);
  }
  public void testSubtract() {
   calculator.add(10);
   calculator.subtract(2);
   assertEquals(calculator.getResult(), 8);
  }
  public void testDivide() {
   calculator.add(8);
   calculator.divide(2);
   assert calculator.getResult() == 5;
  }
  public void testDivideByZero() {
   try {
    calculator.divide(0);
    fail();
   }
   catch (ArithmeticException e) { }
  }
  public void notReadyYetTestMultiply() {
   calculator.add(10);
   calculator.multiply(10);
   assertEquals(calculator.getResult(), 100);
  }
}

   JUnit 4

package JUnit 4;
import calc.Calculator;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import static org.junit.Assert.*;
public class CalculatorTest {
  private static Calculator calculator = new Calculator();
  @Before public void clearCalculator() {
   calculator.clear();
  }
  @Test public void add() {
   calculator.add(1);
   calculator.add(1);
   assertEquals(calculator.getResult(), 2);
  }
  @Test public void subtract() {
   calculator.add(10);
   calculator.subtract(2);
   assertEquals(calculator.getResult(), 8);
  }
  @Test public void divide() {
   calculator.add(8);
   calculator.divide(2);
   assert calculator.getResult() == 5;
  }
  @Test(expected = ArithmeticException.class)
  public void divideByZero() {
   calculator.divide(0);
  }
  @Ignore( "not ready yet")
  @Test
  public void multiply() {
   calculator.add(10);
   calculator.multiply(10);
   assertEquals(calculator.getResult(), 100);
  }
}
三、 包

  首先,你可以看到,JUnit 4使用org.junit.*包而JUnit 3.8使用的是junit.framework.*。當然,爲了向後兼容性起見,JUnit 4jar文件發行中加入了這兩種包。

   四、 繼承

   在中,測試類不必再擴展junit.framework.TestCase;事實上,它們不必須擴展任何內容。但是,JUnit 4中使用的是註解。爲了以一個測試用例方式執行,一個JUnit 4類中至少需要一個@Test註解。例如,如果你僅使用@Before和@After註解而沒有至少提供一個@Test方法來編寫一個類,那麼,當你試圖 執行它時將得到一個錯誤:

java.lang.Exception: No runnable methods.

   五、 斷言(Assert)方法

   因爲在JUnit 4中一個測試類並不繼承自TestCase(在JUnit 3.8中,這個類中定義了assertEquals()方法),所以你必須使用前綴語法(舉例來說,Assert.assertEquals())或者 (由於JDK5.0)靜態地導入Assert類。這樣以來,你就可以完全象以前一樣使用assertEquals方法(舉例來說, assertEquals())。

  另外,在JUnit 4中,還引入了兩個新的斷言方法,它們專門用於數組對象的比較。如果兩個數組包含的元素都相等,那麼這兩個數組就是相等的。

public static void assertEquals(String message, Object[] expecteds, Object[] actuals);
public static void assertEquals(Object[] expecteds, Object[] actuals);

   由於JDK 5.0的自動裝箱機制的出現,原先的12個assertEquals方法全部去掉了。例如,原先JUnit 3.8中的assertEquals(long,long)方法在JUnit 4中要使用assertEquals(Object,Object)。對於assertEquals(byte,byte)、assertEquals (int,int)等也是這樣。這種改進將有助於避免反模式。

  在JUnit 4中,新集成了一個assert關鍵字(見我們的例子中的divide()方法)。你可以象使用assertEquals方法一樣來使用它,因爲它們都拋 出相同的異常(java.lang.AssertionError)。JUnit 3.8的assertEquals將拋出一個junit.framework.AssertionFailedError。注意,當使用assert時, 你必須指定Java的"-ea"參數;否則,斷言將被忽略。

   六、 預設環境(Fixture)

   Fixture是在測試期間初始化和釋放任何普通對象的方法。在JUnit 3.8中,你要使用setUp()來實現運行每一個測試前的初始化工作,然後使用tearDown()來進行每個測試後的清理。這兩個方法在 TestCase類中都得到重載,因此都被唯一定義。注意,我在這個Setup方法使用的是Java5.0內置的@Override註解-這個註解指示該 方法聲明要重載在超類中的方法聲明。在JUnit 4中,則代之使用的是@Before和@After註解;而且,可以以任何命名(在我們的例子中是clearCalculator())來調用這些方法
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章