單元測試應該是簡單和直觀的,而現實中的項目大多都是采用多層方式的,如EJB和hibernate的數據驅動層的中間件技術。
unitils來源于一個嘗試,就是希望能以更務實的方式來看待單元測試......
這個指南會告訴你,什么項目可以使用unitils。 并在這個指導方針頁 中你可以了解到測試的準側和它的特點。如果您想了解如何可以配置unitils ,并得以迅速地啟動,請查看cookbook 。
- unitils的斷言
- unitils的模塊
- 數據庫的測試
- 數據庫的自動測試
- hibernate的測試
- jpa的測試
- spring的測試
- mock object的測試
- 今后的方向
unitils的斷言
在開始這個指南之前我們先說明一下獨立于unitils核心模塊的斷言。在下面的例子中,不需要進行配置,將unitils的jar包和依賴包放在你的classpath下,就可以進行測試了。
通過反射進行斷言
一個典型的單元測試包含了結果值和期望值的比較,unitils提供了斷言的方法以幫助你進行該操作,讓我們看看實例2中對有著id、first name、last name屬性的User類的2個實例的比較
public class User { private long id; private String first; private String last; public User(long id, String first, String last) { this.id = id; this.first = first; this.last = last; } } User user1 = new User(1, "John", "Doe"); User user2 = new User(1, "John", "Doe"); assertEquals(user1, user2);
你期望這個斷言是成功的,因為這兩個實例含有相同的屬性,但是運行的結果并非如此,應為User類并沒有覆寫
equals()方法,所以assertEquals是對兩個實例是否相等進行判斷(user1 == user2)結果導致了比較的失敗。
假設你像如下代碼一樣實現了equals方法
public boolean equals(Object object) { if (object instanceof User) { return id == ((User) object).id; } return false; }
這在你的程序邏輯中是一個合乎邏輯的實現,當兩個User實例擁有相同的id的時候,那么這兩個實例就是相等的。然而這種方式在你的單元測試中并不合適,并不能通過id的相同來認為兩個user是相同的。
User user1 = new User(1, "John", "Doe"); User user2 = new User(1, "Jane", "Smith"); assertEquals(user1, user2);
這個斷言將會成功,但這并不是你所期望的,因此不要使用assertEquals來對兩個對象進行判定是否相等(外覆類和java.lang.String類除外)。要想斷言他們相等,一種方法就是斷言每個屬性相等。
User user1 = new User(1, "John", "Doe"); User user2 = new User(1, "John", "Doe"); assertEquals(user1.getId(), user2.getId()); assertEquals(user1.getFirst(), user2.getFirst()); assertEquals(user1.getLast(), user2.getLast());
unitils提供了一些方法來幫助你執行斷言更加的簡單,通過反射,使用ReflectionAssert.assertRefEquals上面的代碼重寫如下:
User user1 = new User(1, "John", "Doe"); User user2 = new User(1, "John", "Doe"); assertRefEquals(user1, user2);
這個斷言將通過反射對兩個實例中的每個屬性都進行比較,先是id、然后是first name、最后是last name。
如果一個屬性本身也是一個對象,那么將會使用反射進行遞歸比較,這同樣適合與集合、map、和數組之間的比較,他們
的每個元素會通過反射進行比較。如果值是一個基本類型或者是一個外覆類,那么將會使用==進行值的比較,因此下面的斷
言會取得成功
assertRefEquals(1, 1L);
List<Double> myList = new ArrayList<Double>();
myList.add(1.0);
myList.add(2.0);
assertRefEquals(Arrays.asList(1, 2), myList);
寬松的斷言
出于可維護性,這一點是十分重要的,舉例說明:如果你要計算一個帳戶的余額,那你就沒比較檢查這個帳戶的名稱。他只會增加復雜性,使之更難理解。如果你想讓你的測試代碼更容易生存,更容易重構的話,那請確保你斷言的范圍。
寬松的順序
在比較集合和數組的時候你可能并不關心他們中元素的順序,通過使用ReflectionAssert.assertRefEquals方法并配合ReflectionComparatorMode.LENIENT_ORDER參數將忽略元素的順序。
List<Integer> myList = Arrays.asList(3, 2, 1); assertRefEquals(Arrays.asList(1, 2, 3), myList, LENIENT_ORDER);
無視默認
第二種的從寬方式是使用ReflectionComparatorMode.IGNORE_DEFAULTS模式,當這種模式被設置的時候,java
的默認值,如null、0、false將會不參與斷言(忽略)。
舉個例子,如果你有一個User類,該類有著first name,last name,street等屬性,但是你僅僅想對first name
和street進行檢查而忽略其他的屬性。
User actualUser = new User("John", "Doe", new Address("First street", "12", "Brussels")); User expectedUser = new User("John", null, new Address("First street", null, null)); assertRefEquals(expectedUser, actualUser, IGNORE_DEFAULTS);
你所期望忽略的屬性的對象必須放在斷言左邊,如果放在右邊那么依然進行比較。
assertRefEquals(null, anyObject, IGNORE_DEFAULTS); // Succeeds assertRefEquals(anyObject, null, IGNORE_DEFAULTS); // Fails
寬松的日期
第三種從寬處理是ReflectionComparatorMode.LENIENT_DATES,當兩個日期都是值,或者都是null的時候,實際的日期將會被忽略(即斷言為相等)。
Date actualDate = new Date(44444); Date expectedDate = new Date(); assertRefEquals(expectedDate, actualDate, LENIENT_DATES);
assertLenEquals
ReflectionAssert還提供了一種斷言,他提供寬松的順序又提供無視的忽略。
List<Integer> myList = Arrays.asList(3, 2, 1); assertLenEquals(Arrays.asList(1, 2, 3), myList); assertLenEquals(null, "any"); // Succeeds assertLenEquals("any", null); // Fails
屬性斷言
assertLenEquals和assertRefEquals都是比較對象,ReflectionAssert也提供方法對對象的屬性進行比較。(依賴與ONGL)。
一些屬性比較的例子
assertPropertyLenEquals("id", 1, user); //斷言user的id屬性的值是1 assertPropertyLenEquals("address.street", "First street", user); //斷言user的address的street屬性
在這個方式中你期望的值和判定的對象也可以使用集合
assertPropertyLenEquals("id", Arrays.asList(1, 2, 3), users); assertPropertyLenEquals("address.street", Arrays.asList("First street",
"Second street", "Third street"), users);