在JUnit測試中,通常作為POJO的Java Bean都是一組簡單的getter/setter方法,需要測試的不是這些Bean本身,而是對Bean的屬性設置后,測試業務方法是否正常工作,例如,一個注冊用戶的類方法void register(Account account),需要對傳入的Account Bean做初始化設置,然后,根據業務規則決定register方法是否應該執行成功或者拋出IllegalArgumentException。
倘若按照常規的Unit測試,需要考慮用戶輸入的許多種組合,在testXxx()方法中編寫模擬用戶輸入的代碼是冗長而繁瑣的,不如直接通過Swing窗口手動設置Bean的屬性,然后再執行業務方法,這樣,雖然引入了輸入界面,需要人工干預測試過程,卻大大簡化了編寫測試用例的麻煩。
在TCK測試中,對許多UI組件的測試正是采用這種半自動的方法,因為只有測試人員本身才能看到UI測試的結果正確與否,計算機很難判斷一個類似fillRect()的方法到底有沒有在屏幕上正確繪制出來。
基于這種思想,為了測試許多種不同輸入的組合,我們決定編寫一個能根據Bean的屬性自動生成輸入窗口的小工具,以便在Unit測試過程中,能夠由測試人員手動設定Bean的屬性和期望的結果,然后,繼續測試。
為了實現這個目的,我們設計了一個小工具,它能夠實現:
1.根據傳入的Bean自動為每個public setXxx()方法生成輸入框;
2.由測試人員手動設置Bean的屬性,然后,選擇“Success”,“Failure”以決定這次測試的數據應該產生正確或錯誤的結果。
3.可以連續多次循環測試,直到測試人員點擊“End”,結束本次測試。
整個工具被封裝在一個BeanInputDialog類中,它繼承自JFrame,只暴露了一個public static方法。下面,我們以一個Account Bean為例,測試用戶輸入的屬性是否合法。
Account定義了4個字段和一個validate()方法來驗證輸入,如果不符合輸入,validate()方法會拋出IllegalArgumentException。我們編寫一個簡單的TestCase:
package com.crackj2ee.test.util;
import junit.framework.TestCase;
public class AccountTest extends TestCase {
public void testAccount() {
int expect;
for(;;) {
Account account = new Account(); // 待測試的Bean
expect = BeanInputDialog.inputBean(account); // 用戶在此輸入
// 注意:直到用戶輸入完成,inputBean()方法才會返回,它是一個同步方法
// inputBean()方法返回一個int,表示本次測試的期望值
if(expect==BeanInputDialog.EXPECT_END) // 期望測試結束,退出循環
break;
if(expect==BeanInputDialog.EXPECT_SUCCESS) { // 期望測試成功
account.validate();
}
if(expect==BeanInputDialog.EXPECT_FAILURE) { // 期望測試失敗
try {
account.validate();
fail("Not catch IllegalArgumentException!"); // 沒有捕獲到預期的異常
}
catch(IllegalArgumentException e) {
// OK!捕獲到預期的異常
}
}
}
}
}
在Unit測試過程中,可以不斷設置Bean的屬性,以滿足多種組合的測試用例。下面分別輸入合法和非法的用戶名,然后點擊期望值測試:
需要注意的是,由于屬性的獲取是通過反射得到的,考慮到通常的設計原則,我們只讀取public的setXxx()方法。
如果在測試人員輸入前,Bean已經有了一些初始值,這個工具也能通過反射得到相應的getXxx()方法的值。要注意的是,對boolean和Boolean類型的屬性,是根據getXxx()取得而不是根據isXxx()取得的。
目前,可以處理的屬性類型包括java.lang.String,java.util.Date,short,int,long,float,double,boolean及其包裝類型Short,Integer,Long,Float,Double和Boolean。還可以對其擴展,以便支持更多的類型。