概念
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()