這是老調了.... 不過我們的新人沒碰過這種東東,所以只好又再發信給我們的 team 內部看,有興趣的人可以參考我們的 convention:
一般而言,大多數有按照 TDD (Test Driven Development) 開發的程式,最後Production Code 和 Test Code 的比例大約是 1:1。開發了 50000 行,就會有 50000 行的 Test。所以寫 Test 也需要有技巧的組織,以及運用一些 design pattern ,不然到後來會全部亂成一團。Object Mother 是我們 team 裡面用的最多的一 個 pattern,下面是有關 Object Mother Pattern 的資料:
這個站也是 Unit Test 的 pattern 的大集合,首頁在:
Patterns of XUnit Test Automation
這些 pattern 都很短很小,希望大家有空多看,讓寫 Test 變的更輕鬆更有效率。
==================如何使用 Object Mother 分隔線===================
Object Mother 是一種 pattern,怎麼運用就看個人發揮。我們 team 裡運用這個 pattern 已經有一段時間了,現在也有了一些 convention,整理如下:
Object Mother 在我們這簡稱 OM,Class 名字通常取做 xxxOM。
一般而言一個 package 裡面有一個 OM
比方說
org.bioinfo.project ==> ProjectOM org.bioinfo.user ==> UserOM org.bioinfo.user.authority ==> AuthortyOM
這並沒有硬性規定。你要一個 class 寫一個 OM 也行,不過太多 OM並不是很好管理,請自行拿捏。
OM 只存在在 "test" 的 source folder 裡
換句話說 production code 的 folder "src" 不會有 OM,也不會用到 OM 的 method。如果你不小心 "src" 裡用到了,那麼跑 C.I. (Anthill) 時會 compile 不過,直接拿到一個大紅燈。
OM 都需繼承 BaseObjectMother 這個 class
- BaseObjectMother 已經和 BaseDAOTestCase, BaseIntegrationMockStrutsTestCase...等等程式整合好
- BaseObjectMother 也提供 randomInt() randomStr() 之類的 method方便產生亂數的資料
所以你在 OM 可以直接拿到 Spring 的資源,例如 hibernateTemplate,來寫資料庫相關的 Fixture
有興趣的人可以自己去看 BaseObjectMother 這個裡面有什麼東東。如果你發現有一些工具類的 method,常常在很多 OM 裡面會用到,你也可以自己加 method 到 BaseObjectMother 裡。
OM 裡面有兩大類的 method,一個是純物件,另一個是資料庫物件
這兩種分法是我們的 convention,請大家繼續延用。產生純物件類的 method 以 new 為開頭。產生資料庫物件的則以 saved 為開頭
比方說,產生 User 純物件的 method 可如下:
UserOM.newUser () ; UserOM.newUserWithName(String name) ; ProjectOM.newProject(String projectCode) ; ProjectOM.newProject(String projectCode, User projectOwner) ; ...
這些 method 裡面所有涉及到的物件,都不會跟資料庫存取。換句話說,method裡面的物件都是直接 new 產生的,而不是從資料庫取出,或是曾經存到資料庫裡。
而產生資料庫物件則如下:
UserOM.savedUser() ; UserOM.savedUsers(int noOfUsers) ; OrderOM.savedOrder(int orderId, Date orderDate) ; OrderOM.savedOrder(int orderId, int noOfLineItems) ; ...
這一類的寫法完全與純物件 method 相反:它裡面所涉及、所產生的物件全部都要存到資料庫裡。
設計上,純物件類的適合用在一般的測試或是 Struts 的測試上,它的優點是跑起來 快,寫起來也很簡單。另一個資料庫類的,因為跑起來很慢,也不大好寫,所以只適合用在 DAO 或是整合性的測試上,其他地方請勿使用。如果你發現你不是在做 DAO 或是整合性的測試,卻要用到 savedXXX() 的 method,那表示你的 Production Code 或是 Test Code 有地方設計錯了,請重新再思考一次。
OM 的 method 的內容盡可能避免重覆
來看下面 UserOM 的例子:
public static User newUser() { return new User( "myFirstName", "myLastName", "myEmail@com.tw") ; } public static User newUser(String firstName) { return new User( firstName, "myLastName", "myEmail@com.tw") ; }
這兩個 method 很簡單,一個是產生一個預設的假 User,另一個則可以指定假User 的firstName。上面這兩個 method 犯了一個錯誤,就是程式碼重覆,應該要改為:
public static User newUser() { return newUser( "myFirstName" ) ; //重用下面的 method } public static User newUser(String firstName) { return new User( firstName, "myLastName", "myEmail@com.tw") ; }
讓上面的第一個 method 去呼叫另一個變化比較多的第二個 method,達到重用。OM 的一個重要精神便是組織 Fixture,所以OM 裡面的method 也必需善加互相使用,避免重覆,這樣花時間寫的 OM 才有意義。
OM 裡面已經寫好的 method,就不能再改內容
比方說你今天已經寫了一個 OM 的 method:
public static User newUser() { return newUser( "myFirstName" ) ; }
那麼,大概從明天起,你就不能再改這個 method 的內容為:
public static User newUser() { return newUser( "yourFirstName" ) ; //錯誤示範 }
原因是這個 method 一旦寫出來,除了你自己之外,其他人也可能開始用了,你改成別的,別人的測試可能就會 fail。一旦遇到這種狀況,請再加寫另一個method:
public static User newUser() { return newUser( "myFirstName" ) ; //原來的不變 } public static User newAnotherUser() { //多一個新的 return newUser( "yourFirstName" ) ; }
當然你也可以做一個 User newUser(String firstName) 的 method 來解決。
OM 的 method 裡如果有 throw Checked Exception 時,該 method 要改成throw 最大的 Exception
比方說:
public static Order newOrder(int items) throws OrderException { Order order = new Order(); order.calulate(items) ; //這個 method 會 throw OrderException reutrn order ; }
你要改成最大的 Exception:
public static Order newOrder(int items) throws Exception { Order order = new Order(); //...... }
對 Test 來說 OM 裡面如果發生 OrderException 和 Exception 都是意外,不該在 Test 的過程中發生,如果發生就表示 OM 寫錯了,所以寫成小的 Exception或是大的都沒啥差別。而寫成最大的 Exception 可以讓未來維護測試的程式碼更簡單,所以我們推薦直接 throw 最大的 Exception。
下面這種寫法是錯誤的示範:
public static Order newOrder(int items) { Order order = new Order(); try { order.calulate(items) ; //這個 method 會 throw OrderException } catch (OrderException e) { // 錯誤的示範 throw new RuntimeException(e) ; } reutrn order ; }
為什麼這種寫法不適合在 OM 裡用?這裡我就不講了,有興趣的人下次遇到這種狀況時,可以自己寫寫看,體會一下為什麼這樣寫不好。
==============================================================
OK,先這樣子,有問題大家在討論。