Hamcrest是用于编写匹配器对象的框架,允许以声明方式定义“匹配”规则。有许多情况下匹配器是不可估量的,例如UI验证或数据过滤,但是在编写灵活测试的领域中,匹配器反而是最常用的。
简述
Hamcrest是用于编写匹配器对象的框架,允许以声明方式定义“匹配”规则。有许多情况下匹配器是不可估量的,例如UI验证或数据过滤,但是在编写灵活测试的领域中,匹配器反而是最常用的。
Hamcrest还是相对比较简单的,API也相对比较少,就从一个小例子说起吧。
假如要你要测试一个集合中是否包含三个元素中的一个,如果包含则断言真,否则为假。把集合的初始化放在@Before
中,则用JUnit的Assert断言写法如下:
(JUnit4.class) |
assertWithJunitTest方法本身并难以理解,但是你第一眼看到它很可能不太明白它是做什么的,而且代码也不简练,而Hamcrest则正是为了简化断言,可以构建测试表达式的匹配器库。Hamcrest的书写方法如下:
import static org.hamcrest.CoreMatchers.anyOf; |
常用API
hamcrest的匹配器都看起来很好理解,并且还提供了自定义匹配器的接口,可以满足编写代码的需要。主要的API接口有如下:
核心
anything
- 绝对匹配,无论什么情况下都会匹配成功;describedAs
- 添加自定义失败描述;is
- 是否的意思,仅用来改善断言语句的可读性;
逻辑
allOf
- 检查是否包含所有的匹配器,相当于与(&&);anyOf
- 检查是否包含匹配器中的一个,相当于(||);not
- 检查是否与匹配器相反,相当于非(!);
对象
equalTo
- 检查两个对象是否相等;hasToString
- 检查Object.toString;instanceOf
,isCompatibleType
- 检查对象是否是兼容类型;notNullValue
,nullValue
- 检查是否是null值;sameInstance
- 检查对象是否是相同的类型;
Beans
hasProperty
- 检查对象是否有某种属性;
集合
array
- 检查array的元素是否和匹配器描述的相同;hasEntry
,hasKey
,hasValue
- 测试给定的Map是否有特定的实体、键或者值;hasItem
,hasItems
- 测试集合是否有一个或者多个元素;hasItemInArray
- 测试数组中是否有某一元素;
数字
closeTo
- 给定的数字是否接近于给定的值;greaterThan
,greaterThanOrEqualTo
,lessThan
,lessThanOrEqualTo
-给定的数字是否大于、大于等于、小于、小于等于给定的值;
文本
equalToIgnoringCase
- 检查给定的字符串是否与另一字符串在忽略大小写的情况下相同;equalToIgnoringWhiteSpace
- 检查给定的字符串是否与另一字符串在忽略空格的情况下相同;containsString
- 检查给定的字符串是否包含某一字符串;endsWith
- 检查给定的字符串是否以某一字符串结尾;startsWith
- 检查给定的字符串是否以某一字符串开头;
这些匹配器除了可以单独使用外,还可以组合使用,提供更佳精确的匹配。例如:
assertThat(hamcrestTestList, hasItem(anyOf(equalTo("first element"),equalTo("second element"), equalTo("third element")))); |
自定义匹配器
hamcrest除了可以使用上述匹配器以外,还可以自己编写合适的匹配器,更好的进行测试。自定义匹配器需要实现Mather接口和一个适当的工厂方法。自定义匹配器一方面可以达到合适的匹配,另一方面也可以简化断言语句。
要想定义自定义匹配器,则需要实现BaseMatcher<T>
或者TypeSafeMatcher<T>
,需要在build.gradle
文件中dependencies
闭包中引入对应的包:
dependencies { |
自定义匹配器有以下两种实现方法:
实现BaseMatcher<T>
接口
如果实现了BaseMatcher<T>
,那么需要重写matches方法,matches方法内部实现我们的逻辑,即要满足什么条件时匹配器将匹配各种条件,并确定是否匹配成功。比如如果要实现自定义匹配器,判断给定的字符串是否以ham
开头,并且以java
结尾,则自定义匹配器实现方式如下:
public class IsExceptedStringMatcher extends BaseMatcher<String> { |
之后就可以在断言中直接使用自定义匹配器:
(expected = AssertionError.class) |
实现TypeSafeMatcher<T>
接口
如果实现了TypeSafeMatcher <T>
,那么需要重写matchesSafely方法,
protected boolean matchesSafely(T item) |
matchesSafely方法内部同样实现我们的逻辑,该方法绝不能接受一个null对象,它总会接受一个类型为T的参数,该参数已被检查是否为null且永远不能为null。如果我们要在自定义匹配器内部进行是否为null检查,则要继续实现BaseMatcher<T>
接口,如果要自定义一个密码检查的匹配器,要求密码必须包含1个特殊符号(!、"、#、$、%、&、’、(、)、*、+、-、.、/)和1个数字,并且密码长度至少为6,则自定义匹配器实现方式如下:
public class IsStrongPassword extends TypeSafeMatcher<String> { |
接下来你同样可以在测试代码中使用如下的方式使用该匹配器:
(expected = AssertionError.class) |