- 論壇徽章:
- 0
|
診斷 Java 代碼: 設(shè)計(jì)“可測試的”應(yīng)用程序
如何生產(chǎn)出高質(zhì)量的軟件,很大程度上取決于代碼的質(zhì)量。
XP提出:高質(zhì)量的代碼,應(yīng)該是可測試的代碼。所以他提倡 test first design。
XP 推薦的test first design 的開發(fā)過程:
比如說你要寫一個(gè)類,按以下步驟來:
1.寫這個(gè)類的框架,寫的代碼只要能夠進(jìn)行測試就可以了。
2.寫測試代碼,完成測試框架,編譯源程序和測試程序,看到測試運(yùn)行失敗。
3.寫程序,然后運(yùn)行測試,直到所有測試通過。
4.以后修改代碼后,運(yùn)行測試,如果所有測試通過,修改代碼成功。
舉例說明:如果你寫一個(gè)更新數(shù)據(jù)庫的方法 updateThread:
Thread 是論壇里的一篇文章 屬性有 title(標(biāo)題), content(內(nèi)容), notify(是否通知作者)
1.寫這個(gè)類的框架:
class Thread { //Thread Bean
... // 一堆的 get,set 方法
}
class ThreadDAO {
/**
* update Thread from DB
*/
public void updateThread( Thread thread )
throws SQLException, ThreadNotFoundException{
// 里面不寫任何代碼,等到寫完測試后再在此寫代碼
// 現(xiàn)在寫的 ThreadDAO 的代碼只是為了編譯通過。
}
}
2.寫測試代碼,使用 JUnit 作為測試框架
我寫了三個(gè)測試函數(shù):
測試 NULL Object 的情況:testUpdateThreadNULLObject()
測試 Thread Not Found :testUpdateThreadNotFound()
測試 正常的 更新 :testUpdateThreadNormal()
public class ThreadDAOTest extends TestCase {
...
public void testUpdateThreadNULLObject() {
try{
final ThreadDAO dao = new ThreadDAO();
final Thread pt = null;
dao.updateThread(pt);
fail ("Except NULL Object" ;
}catch( final SQLException sqlException ){
fail("Unexpected excetpion: " + sqlException );
}catch( final ThreadNotFoundException threadNotFoundException ){
fail("Unexpected excetpion: " + threadNotFoundException );
}catch ( final IllegalArgumentException IllegalArgumentException ){
//pass
}
}
public void testUpdateThreadNotFound() throws Exception {
try{
final ThreadDAO dao = new ThreadDAO();
final Thread pt = dao.findByUID( 1 );
pt.setID( 100 );
dao.updateThread(pt);
fail("Expect ThreadNotFoundException" ;
}catch( final SQLException sqlException ){
fail("Unexpected excetpion: " + sqlException );
}catch( final ThreadNotFoundException threadNotFoundException ){
//Pass
}
}
public void testUpdateThreadNormal() throws Exception {
ThreadDAO dao = new ThreadDAO();
Thread pt = dao.findByUID(1);
// store old values
String oldTitle = pt.getTitle();
String oldContent = pt.getContent();
boolean oldNotify = pt.getNotify();
// change values
pt.setTitle ( oldTitle + "AddedTitle." ;
pt.setContent ( oldContent+ "AddedContent." ;
pt.setNotify(!oldNotify);
// update
dao.updateThread(pt);
// query again
pt = dao.findByUID(1);
assertEquals("Update Thread Title", oldTitle + "AddedTitle.", pt.getTitle());
assertEquals("Update Thread Content", oldContent + "AddedContent.", pt.getContent());
assertEquals("Update thread Notify", !oldNotify, pt.getNotify());
// back to original value
// change values
pt.setTitle( oldTitle );
pt.setContent( oldContent );
pt.setNotify( oldNotify );
// update
dao.updateThread(pt);
}
編譯,運(yùn)行測試,一定失敗,呵呵,這就是現(xiàn)在我們的目標(biāo),測試框架已經(jīng)完成
3. 開始寫 ThreadDAO.updateThread() 方法
/**
* Update title , content, notify of a thread
*/
public void updateThread( final Thread thread )
throws SQLException, ThreadNotFoundException{
Validation.validateNotNull( thread );
final Thread newThread = thread;
findByUID( newThread.getID() );
Connection conn = getConnection();
PreparedStatement stat
= conn.prepareStatement( "update " + ... );
stat.setString( 1, newThread.getTitle() );
stat.setString( 2, newThread.getContent() );
stat.setString( 3, notifyString );
stat.execute();
stat.close();
conn.close();
}
然后運(yùn)行測試,如果測試失敗,繼續(xù)修改代碼,直到測試成功。
4. 如果以后對代碼做修改,比如方法內(nèi)部的優(yōu)化。。。只要重新運(yùn)行測試,
就可以很有信心的說,我的代碼沒有問題。
這有幾個(gè)好處
寫程序是可以知道什么時(shí)候?qū)懘a結(jié)束了,以運(yùn)行測試是否成功為標(biāo)準(zhǔn)
對自己的代碼很有信心,特別是對將來的改動,不用害怕。
好的代碼是可測試的代碼,寫測試的過程,會發(fā)現(xiàn)原來設(shè)計(jì)中不足的地方,可加以修改
測試可以作為代碼的文檔,要知道代碼作了些什么,看測試代碼就可以了。
測試工具:
JUnit(單元測試框架類庫)
Ant(Java 項(xiàng)目管理軟件)
|
|