認識Hamcrest
Hamcrest是一個匹配工具,提供了常用的匹配的工具方法,主要在自動化測試匹配場景中使用,不過也可以用在其他場景下。下面是判斷兩個對象是否相等(如果是數組,則數組里面的元素需要全部相等并且順序相同)的一個例子。
再看一個測試場景下的使用
個人認為hamcrest主要帶來了兩個好處
1.?算法的沉淀,將一些通用的匹配算法沉淀起來(其他人也可以擴展自己的匹配算法),可以方便的進行復用
2. 提供了更好的語義,可讀性更強
內置的匹配器
從hamcrest包結構看主要有如下不同功能的內置匹配器
beans:主要包含屬性匹配;屬性值匹配等
collection:集合相關的匹配,例如是否包含某個元素,數組里的元素順序等
comparator:比較器算法
core:提供了一些基礎的匹配器,例如is,equals,any,all,contains等
io:文件的基本匹配
number:簡單的數據比較
object:對象的一些匹配算法
text:字符串相關的匹配算法
xml:xmlpath的匹配算法
具體的每個匹配器可以通過源碼查看https://github.com/hamcrest/JavaHamcrest
Hamcrest的結構
整體的架構
不過從功能來劃分個人覺得新增一個matchers的包結構更清晰些
主要的接口與類
Matcher
其中matches方法并沒有使用泛型而是Object,作者主要是想把匹配器(Matcher)與被匹配對象(matchee)分開,匹配器可以與被匹配對象類型不一致,具體的匹配類型校驗由匹配器邏輯去控制,類似Object.equals,詳見https://github.com/hamcrest/JavaHamcrest/pull/200#ref-pullrequest-548558670
另外_dont_implement_Matcher___instead_extend_BaseMatcher_這個方法是因為之前接口不支持default方法,為了方便之后擴展,因此加了這個方法提醒大家不要直接實現Matcher,而是通過繼承BaseMatcher
函數的柯里化
另外Matcher也使用了函數的curry化,使用方法與java中的正則表達式匹配方式比較類似,將一部分參數作為匹配器的屬性,然后去對參數對象進行匹配,這樣的好處主要是:
1、可以減少參數數量,同時使每個匹配器(matcher)職責更單一,如果使用工具方法(例如MatcherUtil.equals(Object a, Object b)),則該工具方法會膨脹的很快;
2、通過接口的統一,使用更方便,而且可以通過組合實現豐富的功能
3、可以對部分參數進行預處理,提高后續處理匹配的效率
可以參考下函數的curry化
Condition
Condition主要用在處理鏈中,下一步的輸入是上一步的結果,就跟linux中的管道一樣。Condition有兩個核心方法,3個內部子類型(2個內部類,1個內部接口)
其中Step類似linux中的一個個命令,Condition與Matched,NotMatched組成了一個管道將各個Step串聯起來,方式也跟之前說的curry化類似。
例如有一個場景:輸入一個整數
1、第一步假如值大于1,值加1,否則匹配失敗
2、第二步假如第一步值大于5,乘以2,否則匹配失敗
3、第三步判斷第二步值大于10,小于20
那我們實現一個Matcher如下
Condition主要可以沉淀Step操作,達到復用的效果(不然直接使用一個大方法簡單明了,不需要繞來繞去了)
TypeSafeMatcher
由于Matcher接口的match方法是Object,在一些需要特定類型匹配的場景下為了防止類型轉換異常,因此提供了TypeSafeMatcher,該類會根據matchesSafely方法的參數類型先做一次類型判斷,只有類型判斷通過才執行后續匹配操作,例如IsEmptyString;需要注意的是由于類型擦除,通過泛型計算出來的expectedType都是Object,例如HasProperty,因此其實通過泛型聲明的匹配器依然不是類型安全的,例如下例中的整數1還是會被HasProperty的matchesSafely方法執行到。
另外還有一點值得注意是TypeSafeMatcher無法匹配null,如果需要匹配null需要自行處理。
歡迎關注 HelloWorld4J