注意DatabaseFixture的createProxy()方法,它將一個普通的DAO對象包裝為在事務(wù)范圍內(nèi)執(zhí)行的代理對象,即對于一個普通的DAO對象的方法調(diào)用前后,自動地開啟事務(wù)并根據(jù)異常情況提交或回滾事務(wù)。
下面是UserDaoImpl的單元測試類:
public class UserDaoImplTest extends DatabaseFixture {
private UserDao userDao = new UserDaoImpl();
private UserDao proxy = (UserDao)createProxy(userDao);
@Test
public void testQueryUser() {
User user = newUser("test");
proxy.createUser(user);
User t = proxy.queryUser("test");
assertEquals(user.getEmail(), t.getEmail());
}
}
注意到UserDaoImplTest持有兩個UserDao引用,userDao是普通的UserDaoImpl對象,而proxy則是將userDao進行了事務(wù)封裝的對象。
由于UserDaoImplTest從DatabaseFixture繼承,因此,@Before方法在每個@Test方法調(diào)用前自動調(diào)用,這樣,每個@Test方法執(zhí)行前,數(shù)據(jù)庫都是一個經(jīng)過初始化的“干凈”的表。
對于普通的測試,如UserDao.queryUser()方法,直接調(diào)用proxy.queryUser()即可在事務(wù)內(nèi)執(zhí)行查詢,獲得返回結(jié)果。
對于異常測試,例如期待一個ResourceNotFoundException,不能直接調(diào)用proxy.queryUser()方法,否則,將得到一個UndeclaredThrowableException:
對DAO編寫單元測試 圖-3
這是因為通過反射調(diào)用拋出的異常被代理類包裝為UndeclaredThrowableException,因此,對于異常測試,只能使用原始的userDao對象配合TransactionCallback實現(xiàn):
@Test(expected=ResourceNotFoundException.class)
public void testQueryNonExistUser() throws Exception {
new TransactionCallback() {
protected Object doInTransaction() throws Exception {
userDao.queryUser("nonexist");
return null;
}
}.execute();
}
到此為止,對DAO組件的單元測試已經(jīng)實現(xiàn)完畢。下一步,我們需要使用HibernateTool自動生成數(shù)據(jù)庫腳本,免去維護SQL語句的麻煩。相關(guān)的Ant腳本片段如下:
<target name="make-schema" depends="build" description="create schema">
<taskdef name="hibernatetool" classname="org.hibernate.tool.ant.HibernateToolTask">
<classpath refid="build-classpath"/>
</taskdef>
<taskdef name="annotationconfiguration" classname="org.hibernate.tool.ant.AnnotationConfigurationTask">
<classpath refid="build-classpath"/>
</taskdef>
<annotationconfiguration configurationfile="$/hibernate.cfg.xml"/>
<hibernatetool destdir="$">
<classpath refid="build-classpath"/>
<annotationconfiguration configurationfile="$/hibernate.cfg.xml"/>
<hbm2ddl
export="false"
drop="true"
create="true"
delimiter=";"
outputfilename="schema.sql"
destdir="$"
/>
</hibernatetool>
</target>
完整的Ant腳本以及Hibernate配置文件請參考項目工程源代碼。
利用HSQLDB,我們已經(jīng)成功地簡化了對DAO組件進行單元測試。我發(fā)現(xiàn)這種方式能夠找出許多常見的bug:
HQL語句的語法錯誤,包括SQL關(guān)鍵字和實體類屬性的錯誤拼寫,反復(fù)運行單元測試可以不斷地修復(fù)許多這類錯誤,而不需要等到通過Web頁面請求而調(diào)用DAO時才發(fā)現(xiàn)問題;
傳入了不一致或者順序錯誤的HQL參數(shù)數(shù)組,導(dǎo)致Hibernate在運行期報錯;
一些邏輯錯誤,包括不允許的null屬性(常常由于忘記設(shè)置實體類的屬性),更新實體時引發(fā)的數(shù)據(jù)邏輯狀態(tài)不一致。
總之,單元測試需要根據(jù)被測試類的實際情況,編寫簡單有效的測試用例。本文旨在給出一種編寫DAO組件單元測試的有效方法。