From Gossip@caterpillar

JUnit Gossip: Failure、Error

在運行TestRunner執行您的測試時,您會發現到有FailureError兩種測試尚未通過的訊息。

Failure指的是預期的結果與實際運行單元的結果不同所導致,例如當您使用assertEquals()或其它assertXXX()方法斷言失敗時,就會回報Failure,這時候您要檢查您的單元方法中的邏輯設計是否有誤。

Error指的是您程式沒有考慮到的情況,在斷言之前程式就因為某種錯誤引發例外而終止,例如在單元中存取某個陣列,因為存取超出索引而引發 ArrayIndexOutOfBoundsException,這會使得單元方法無法正確完成,在測試運行到asertXXXX()前就提前結束,這時 候您要檢查您的單元方法中是否有未考慮到的情況而引發流程突然中斷。

來看個實際的例子,如果您設計了下面的測試案例:

  • ObjectArrayTest.java
package onlyfun.caterpillar.test;

import onlyfun.caterpillar.ObjectArray;
import junit.framework.TestCase;

public class ObjectArrayTest extends TestCase {
public void testAdd() {
ObjectArray objArr = new ObjectArray();
Object testObj = new Object();
Object obj = objArr.setObject(0, testObj);
assertEquals(testObj, obj);
}

public static void main(String[] args) {
junit.textui.TestRunner.run(ObjectArrayTest.class);
}
}

然後根據這個測試案例,您撰寫了ObjectArray類別:
  • ObjectArray.java
package onlyfun.caterpillar;

public class ObjectArray {
private Object[] objs;

public Object setObject(int i, Object o) {
return objs[i];
}
}

接下來運行TestRunner,您發現程式回報了Error:
.E
Time: 0
There was 1 error:
1) testAdd(onlyfun.caterpillar.test.ObjectArrayTest)
      java.lang.NullPointerException
....
....
FAILURES!!!
Tests run: 1,  Failures: 0,  Errors: 1


仔細看一下您所設計的ObjectArray類別,顯然程式撰寫的太勿忙,忘了初始化Object陣列了,因而引發了 NullPointerException,這是程式設計上的錯誤,使得尚未進行斷言之前就引發例外中斷,所以修正一下ObjectArray,提供一個 建構函式,預設產生長度為10的陣列:
  • ObjectArray.java
package onlyfun.caterpillar;

public class ObjectArray {
private Object[] objs;

public ObjectArray() {
objs = new Object[10];
}

public Object setObject(int i, Object o) {
return objs[i];
}
}

再運行一次TestRunner,這次Error沒了,但是有Failure:
.F
Time: 0
There was 1 failure:
1) testAdd(onlyfun.caterpillar.test.ObjectArrayTest)
     junit.framework.AssertionFailedError:
      expected:<java.lang.Object@10b30a7> but was:<null>
....
....
FAILURES!!!
Tests run: 1,  Failures: 1,  Errors: 0


這表示可以執行到assertEquals()完畢,但是斷言失敗,所以再設計程式以消除Failure:
  • ObjectArray.java
package onlyfun.caterpillar;

public class ObjectArray {
private Object[] objs;

public ObjectArray() {
objs = new Object[10];
}

public Object setObject(int i, Object o) {
objs[i] = o;
return objs[i];
}
}

再次運行TestRunner,這次應該可以通過測試了。

如果您的單元在執行時,使用throws聲明會丟出某些例外,例如IOException,您希望可以在testXXXX()中測試例外是否真的被丟出, 您可以使用try.....catch來處理,下面這個程式是個實例,假設java.io.FileReader是您所設計的類別,您提供一個測試檔案 test.txt:

  • FileReaderTest.java
package onlyfun.caterpillar.test;

import java.io.*;
import junit.framework.TestCase;

public class FileReaderTest extends TestCase {
public void testClose() throws IOException {
FileReader reader = new FileReader("test.txt");
reader.close();
try {
reader.read();
fail("reading file after closed" +
" and didn't throw IOException");
}
catch(IOException e) {
assertTrue(true);
}
}

public static void main(String[] args) {
junit.textui.TestRunner.run(FileReaderTest.class);
}
}

您所測試的可能是一個預期會丟出的例外,您想要看看當錯誤的情況成立時,是不是真會丟出例外,例如 testClose() 測試FileReader 在close()之後如果再read(),是不是會如期丟出IOException,您先行在方法中用try....catch捕捉了這個例外,如果沒有 如期丟出例外,則不會被catch捕捉,而程式流程繼續往下,執行到 fail() 陳述,這表示例外處理沒有發生,此時主動丟出一個Failure,表示程式的執行並不如您所預期的。