概念
Hamcrest是用於編寫匹配器對象的框架。他提供了一套匹配符Matcher,這些匹配符更接近自然語言,可讀性高,更加靈活。Hamcrest還有很好的可擴展性,能夠創建自定義的匹配器。
支持語言
Hamcest支持多種語言,在Hamcest 官網便可以看到:http://hamcrest.org/JavaPython
- Ruby
- Objective-C
- PHP
- Erlang
- Swift
示例
from hamcrest import * import unittest class BiscuitTest(unittest.TestCase): def testEquals(self): theBiscuit = 'Ginger' myBiscuit = 'Ginger' assert_that(theBiscuit, equal_to(myBiscuit)) if __name__ == '__main__': unittest.main()
assert_that函數是用於測試斷言的語句。 在如上示例中,theBiscuit 是第一個方法參數,第二個方法參數是對象的匹配器,這里使用的是Python中 ==運算符檢查一個對象與另一個對象相等。
Hamcres API
在python中pyHamcrest屬於第三方庫,使用時需要安裝。
Hamcrest在python中提供的API:
對象
- equal_to - 匹配相等對象
- has_length - 長度匹配 len()
- has_property - 匹配給定名稱的屬性值
- has_properties - 匹配具有所給定屬性的對象
- has_string - 匹配字符串 str()
- instance_of - 匹配對象類型
- none, not_none - 匹配none或not none
- same_instance - 匹配相同的對象
- calling, raises - 封裝一個方法調用並斷言它引發異常
數字
- close_to - 匹配接近給定的數字值
- greater_than, greater_than_or_equal_to, less_than, less_than_or_equal_to - 匹配數字順序
文本
- contains_string - 匹配部分字符串
- ends_with - 匹配字符串的結尾
- equal_to_ignoring_case - 匹配完整的字符串但忽略大小寫
- equal_to_ignoring_whitespace - 匹配完整的字符串,但忽略多余的空格
- matches_regexp - 使用正則表達式匹配字符串
- starts_with - 匹配字符串的開頭
- string_contains_in_order - 按相對順序匹配字符串的各個部分
邏輯
- all_of - 如果所有匹配器都匹配才匹配,像Java里的&&
- any_of - - 如果任何匹配器匹配就匹配,像Java里的||
- anything - 匹配任何東西,如果不關心特定值則在復合匹配器中很有用
- is_not, not_ -如果包裝的匹配器不匹配器時匹配,反之亦然
序列
- contains - 完全匹配整個序列
- contains_inanyorder - 以任何順序匹配整個序列
- has_item - 只要給定項目在序列中出現則匹配
- has_items - 如果所有給定項以任意順序出現在序列中則匹配
- is_in - 在給定順序中如果給定項出現則匹配
- only_contains - 在給定順序中如果序列的項目出現則匹配
- empty - 如果序列為空則匹配
字典
- has_entries - 匹配具有鍵值對列表的字典
- has_entry - 匹配包含鍵值對的字典
- has_key - 使用鍵匹配字典
- has_value - 使用值匹配字典
裝飾器
- calling - 在延遲中封裝一個可調用對象,在后續的調用行為中匹配
- raises - 確保延遲調用可以按預期方式引發
- described_as - 添加一個定制的失敗表述裝飾器
- is_ - 改進可讀性裝飾器
這些匹配器中的許多參數不僅接受匹配值,還接受另一個匹配器,因此可以組合匹配器以提高靈活性。 例如,only_contains(less_than(5))將匹配每個小於5項目的任何序列。
自定義匹配器
PyHamcrest捆綁了許多有用的匹配器,但是在我們使用時會發現有時需要創建自己的匹配器來滿足測試需求。一般來說, 當一段代碼重復測試同一組屬性(以及在不同測試中)並且希望將該代碼段捆綁到一個斷言中時, 通過編寫自己的匹配器可以消除代碼重復,並使測試更具可讀性!
編寫一個匹配器用來測試日歷日期是不是在星期六。實現后希望使用的結果:
def testDateIsOnASaturday(self): d = datetime.date(2008, 4, 26) assert_that(d, is_(on_a_saturday()))
代碼實現:
from hamcrest.core.base_matcher import BaseMatcher from hamcrest.core.helpers.hasmethod import hasmethod class IsGivenDayOfWeek(BaseMatcher): def __init__(self, day): self.day = day # Monday is 0, Sunday is 6 def _matches(self, item): if not hasmethod(item, 'weekday'): return False return item.weekday() == self.day def describe_to(self, description): day_as_string = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'] description.append_text('calendar date falling on ') \ .append_text(day_as_string[self.day]) def on_a_saturday(): return IsGivenDayOfWeek(5)
對於Matcher的實現,使用_matches方法,在確認參數有這樣一個方法並且在測試失敗時使用 describe_to 方法來生成失敗消息。下面是一個斷言失敗的消息示例:
assert_that(datetime.date(2008, 4, 6), is_(on_a_saturday()))
消息失敗后給出的提示:
AssertionError: Expected: is calendar date falling on Saturday got: <2008-04-06>
將這個匹配器保存在名為 isgivendayofweek的MODULE 中。 以后可以通過導入函數 on_a_saturday 在測試中使用:
from hamcrest import * import unittest from isgivendayofweek import on_a_saturday class DateTest(unittest.TestCase): def testDateIsOnASaturday(self): d = datetime.date(2008, 4, 26) assert_that(d, is_(on_a_saturday())) if __name__ == '__main__': unittest.main()
