Hamcrest 总结
Junit
JUnit框架用一組assert方法封裝了一些常用的斷言。這些assert方法可以幫我們簡化單元測試的編寫。這樣的話,Junit就可以根據這些斷言是否拋出 AssertionFailedError 錯誤來判斷測試用例的執行結果。
?
Hamcrest
使用過Junit 的應該有過體驗:在實際開發中,一些基本的斷言,如eqaul,null,true它們的可讀性并不是很好。而且很多時候我們要比較對象、集合、Map等數據結構。這樣我們要么進行大段的字段獲取再斷言。或者干脆自己編寫表達式并斷言其結果。
JUnit4.4引入了Hamcrest框架,Hamcest提供了一套匹配符Matcher,這些匹配符更接近自然語言,可讀性高,更加靈活。
Hamcrest 提供了大量被稱為“匹配器”的方法。其中每個匹配器都設計用于執行特定的比較操作。Hamcrest的可擴展性很好,讓你能夠創建自定義的匹配器。最重要的是,JUnit也包含了Hamcrest的核心,提供了對Hamcrest的原生支持,可以直接使用Hamcrest。當然要使用功能齊備的Hamcrest,還是要引入對它的依賴。
?
看個對比例子,前者使用Junit的 斷言,后者使用 Hamcrest的斷言。
?
@Testpublic void test_with_junit_assert() {int expected = 51;int actual = 51;assertEquals("failure - They are not same!", expected, actual);}@Testpublic void test_with_hamcrest_assertThat() {int expected = 51;int actual = 51;assertThat("failure - They are not same!", actual, equalTo(expected));}個人感覺有兩個明顯的區別:
1. 參數順序。兩者的expected 和 actual 前后順序是相反的。
2. Hamcrest 幾乎總是直接使用對象。它的語法更符合函數式編程的風格。
這點很好理解了,Junit 總是獲取值后再比較,因為比較的是簡單的值,因此被比較的放在前面更符合習慣。
而Hamcrest 是直接對測試結果的對象進行一些更復雜的匹配。
?
?
支持語言
Hamcrest 支持以下幾種語言,詳情見http://hamcrest.org/
Java
Python
Ruby
Objective-C
PHP
Erlang
Swift
?
?
Hamcrest匹配器
Hamcrest 提供了很強大的一些api 供我們進行測試斷言。
?
?
核心:anything - 總是匹配,如果你不關心測試下的對象是什么是有用的describedAs - 添加一個定制的失敗表述裝飾器is - 改進可讀性裝飾器 - 見下 “Sugar” 邏輯:allOf - 如果所有匹配器都匹配才匹配,像Java里的&&anyOf - 如果任何匹配器匹配就匹配,像Java里的||not - 如果包裝的匹配器不匹配器時匹配,反之亦然 對象:equalTo - 測試對象相等使用Object.equals方法hasToString - 測試Object.toString方法instanceOf, isCompatibleType - 測試類型notNullValue, nullValue - 測試nullsameInstance - 測試對象實例 Beans:hasProperty - 測試JavaBeans屬性 集合:array - 測試一個數組元素test an array’s elements against an array of matchershasEntry, hasKey, hasValue - 測試一個Map包含一個實體,鍵或者值hasItem, hasItems - 測試一個集合包含一個元素hasItemInArray - 測試一個數組包含一個元素 數字:closeTo - 測試浮點值接近給定的值greaterThan, greaterThanOrEqualTo, lessThan, lessThanOrEqualTo - 測試次序 文本:equalToIgnoringCase - 測試字符串相等忽略大小寫equalToIgnoringWhiteSpace - 測試字符串忽略空白containsString, endsWith, startsWith - 測試字符串匹配這些API 幾乎覆蓋了我們測試斷言的所有情況。再提供良好閱讀性的情況下,減少了一些取值、循環、類型判斷等代碼的編寫。
?
?
不足
這里說的不足,只是本人使用過程中遇到的問題。如果大牛發現Hamcrest 現有的api 支持,歡迎指正。
?
hasProperty
先看一個復雜的層級結構。
?
public class MyPerson {private Address address;private String name;private String fullName;// ... }?
其中 Address 類如下:
?
public class Address {private String companyAddress;private String personalAddress;// ... }這里我們測試一個person對象,有 companyAddress 屬性。
?
?
@Testpublic void test_with_complex_property() {MyPerson p = new MyPerson();Address address = new Address();address.setCompanyAddress("");p.setAddress(address);assertThat("failure has no address property !", p, hasProperty("address"));// would failed// assertThat("failure has no address.companyAddress !", p, hasProperty("address.companyAddress"));}這個單元測試的第二個斷言出錯。也就是說斷言 hasProperty 時,不能跨層。這點就沒有?rest-assured 的API 用著那么方便了。
?
這里我想了個辦法如下:
?
assertThat("failure has no address property !", p, hasProperty("address", hasProperty("companyAddress")));進行了一次嵌套后,就可以滿足我們的需要了。
?
集合的泛型
?
這里我們要處理集合,斷言每個元素都有屬性:
?
@Testpublic void test_with_list_generics() {List persons = new ArrayList<MyPerson>();MyPerson p = new MyPerson();p.setName("KaKa");persons.add(p);MyPerson p2 = new MyPerson();p2.setName("Hust");persons.add(p2);// compile error// assertThat("failure has no address property !", persons, everyItem(hasProperty("address")));assertThat("failure has no address property !", (List<Object>)persons, everyItem(hasProperty("address")));}這我就沒法理解了。顯然,被注釋的那句斷言直接編譯失敗!然后向上轉型后,反而成功。
?
對象自身屬性比較
還用上面的例子,每個 person對象 都有 name 和 fullName 兩個屬性,斷言?fullName?是startwith name 屬性。
@Testpublic void test_with_list_compare_with_self() {// 加上 類型List persons = new ArrayList<MyPerson>();MyPerson p = new MyPerson();p.setName("KaKa");p.setFullName("KaKa Zhang");persons.add(p);MyPerson p2 = new MyPerson();p2.setName("Hust");p.setFullName("Hust Zhang");persons.add(p2);// 這里沒法寫, person對象 屬性 startsWith 另一個屬性assertThat((List<Object>)persons, everyItem(hasProperty("name", startsWith(""))));}這時候只能循環來斷言,某個屬性startwWith 另一個屬性了。
?
?
@Testpublic void test_with_list_compare_with_self() {// 加上 類型List<MyPerson> persons = new ArrayList<MyPerson>();MyPerson p = new MyPerson();p.setName("KaKa");p.setFullName("KaKa Zhang");persons.add(p);MyPerson p2 = new MyPerson();p2.setName("Hust");p2.setFullName("Hust Zhang");persons.add(p2);// 還是用循環來做for (MyPerson person : persons) {assertThat(person.getFullName().startsWith(""), is(true));}}上面的這幾個時我遇到幾個情況。如果大牛有更好的做法,歡迎指點。
?
?
?
?
?
?
?
?
?
?
?
總結
以上是生活随笔為你收集整理的Hamcrest 总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: socat使用指南:3:5种常见的使用方
- 下一篇: AjaxControlToolkit A