美文网首页
单元测试——Hamcrest匹配器框架

单元测试——Hamcrest匹配器框架

作者: 漫步无法人生 | 来源:发表于2020-05-04 10:21 被阅读0次

    一、Hamcrest是什么?

    Hamcrest is a library of matchers, which can be combined in to create flexible expressions of intent in tests.

    Hamcrest 是一个为了测试为目的,且能组合成灵活表达式的匹配器类库。

    二、为什么要用Hamcrest匹配器框架

    Hamcrest的目标是使测试尽可能的提高可读性.例如is()方法其实就是equalTo()的包装方法.

    三、常用方法介绍

        @Test
        public void testHamcrestMatchers() {
            // 核心匹配
            // allOf: 所有条件都必须满足,相当于&&
            assertThat("myname", allOf(startsWith("my"), containsString("name")));
            // anyOf: 其中一个满足就通过, 相当于||
            assertThat("myname", anyOf(startsWith("na"), containsString("name")));
            // both: &&
            assertThat("myname", both(containsString("my")).and(containsString("me")));
            // either: 两者之一
            assertThat("myname", either(containsString("my")).or(containsString("you")));
            // everyItem: 每个元素都需满足特定条件
            assertThat(Arrays.asList("my", "mine"), everyItem(startsWith("m")));
            // hasItem: 是否有这个元素
            assertThat(Arrays.asList("my", "mine"), hasItem("my"));
            // hasItems: 包含多个元素
            assertThat(Arrays.asList("my", "mine", "your"), hasItems("your", "my"));
            // is: is(equalTo(x))或is(instanceOf(clazz.class))的简写
            assertThat("myname", is("myname"));
            assertThat("mynmae", is(String.class));
            // anything(): 任何情况下,都匹配正确
            assertThat("myname", anything());
            // not: 否为真,相当于!
            assertThat("myname", is(not("you")));
            // nullValue(): 值为空
            String str = null;
            assertThat(str, is(nullValue()));
            // notNullValue(): 值不为空
            String str2 = "123";
            assertThat(str2, is(notNullValue()));
     
            
            // 字符串匹配
            // containsString:包含字符串
            assertThat("myname", containsString("na"));
            // stringContainsInOrder: 顺序包含,“my”必须在“me”前面
            assertThat("myname", stringContainsInOrder(Arrays.asList("my", "me")));
            // endsWith: 后缀
            assertThat("myname", endsWith("me"));
            // startsWith: 前缀
            assertThat("myname", startsWith("my"));
            // isEmptyString(): 空字符串
            assertThat("", isEmptyString());
            // equalTo: 值相等, Object.equals(Object)
            assertThat("myname", equalTo("myname"));
            assertThat(new String[] {"a", "b"}, equalTo(new String[] {"a", "b"}));
            // equalToIgnoringCase: 比较时,忽略大小写
            assertThat("myname", equalToIgnoringCase("MYNAME"));
            // equalToIgnoringWhiteSpace: 比较时, 首尾空格忽略, 比较时中间用单个空格
            assertThat(" my \t name ", equalToIgnoringWhiteSpace(" my name "));
            // isOneOf: 是否为其中之一
            assertThat("myname", isOneOf("myname", "yourname"));
            // isIn: 是否为其成员
            assertThat("myname", isIn(new String[]{"myname", "yourname"}));
            // toString() 返回值校验
            assertThat(333, hasToString(equalTo("333")));
           
            
            // 数值匹配
            // closeTo: [operand-error, operand+error], Double或BigDecimal类型
            assertThat(3.14, closeTo(3, 0.5));
            assertThat(new BigDecimal("3.14"), is(closeTo(new BigDecimal("3"), new BigDecimal("0.5"))));
            // comparesEqualTo: compareTo比较值
            assertThat(2, comparesEqualTo(2));
            // greaterThan: 大于
            assertThat(2, greaterThan(0));
            // greaterThanOrEqualTo: 大于等于
            assertThat(2, greaterThanOrEqualTo(2));
            // lessThan: 小于
            assertThat(0, lessThan(2));
            // lessThanOrEqualTo: 小于等于
            assertThat(0, lessThanOrEqualTo(0));
            
            
            
            // 集合匹配
            // array: 数组长度相等且对应元素也相等
            assertThat(new Integer[]{1, 2, 3}, is(array(equalTo(1), equalTo(2), equalTo(3))));
            // hasItemInArray: 数组是否包含特定元素
            assertThat(new String[]{"my", "you"}, hasItemInArray(startsWith("y")));
            // arrayContainingInAnyOrder, 顺序无关,长度要一致
            assertThat(new String[]{"my", "you"}, arrayContainingInAnyOrder("you", "my"));
            // arrayContaining:  顺序,长度一致
            assertThat(new String[]{"my", "you"}, arrayContaining("my", "you"));
            // arrayWithSize: 数组长度
            assertThat(new String[]{"my", "you"}, arrayWithSize(2));
            // emptyArray: 空数组
            assertThat(new String[0], emptyArray());
            // hasSize: 集合大小
            assertThat(Arrays.asList("my", "you"), hasSize(equalTo(2)));
            // empty: 空集合
            assertThat(new ArrayList<String>(), is(empty()));
            // isIn: 是否为集合成员
            assertThat("myname", isIn(Arrays.asList("myname", "yourname")));
           // Map匹配
            Map<String, String> myMap = new HashMap<String, String>();
            myMap.put("name", "john");
            // hasEntry: key && value匹配
            assertThat(myMap, hasEntry("name", "john"));
            // hasKey: key匹配
            assertThat(myMap, hasKey(equalTo("name")));
            // hasValue: value匹配
            assertThat(myMap, hasValue(equalTo("john")));
        }
    

    详细请看: Hamcrest API

    四、自定义Hamcrest匹配器

    1.通过FeatureMatcher自定义Hamcrest匹配器

    创建Hamcrest匹配器

    我们自定义一个为String提供长度的匹配器,需要利用FeatureMatcher类,封装一个现有的匹配器,用来决定给定的被测对象的哪个字段匹配,并且提供丰富的错误信息.FeatureMatcher的构造函数有下列参数:

    • 我们想要包装的匹配器
    • 对我们测试的功能的描述(在错误信息会有体现)
    • 测试功能的名字(在错误信息会有体现)

    我们必须重写featureValueOf(T actual),它的返回值将传入matchesSafely()方法进行匹配判断.

    public static Matcher<String> length(Matcher<? super Integer> matcher) {
        return new FeatureMatcher<String, Integer>(matcher,
                "a String of length that", "length") {
            @Override
            protected Integer featureValueOf(String actual) {
                return actual.length();
            }
        };
    }
    
    测试

    使用你刚才创建的自定义匹配器验证"Gandalf"的长度为8

    @Test
    public void fellowShipOfTheRingShouldContainer7() {
        assertThat("Gandalf", length(is(8)));
    }
    

    使用TypeSafeMatcher自定义匹配器

    我们可以对TypeSafeMatcher进行扩展.与BaseMatcher相比TypeSafeMatcher可以自动的检查null值, 在被委派到matchesSafely()方法之前检查类型并进行适当的转换.下面定义了一个检查一个字符串是否匹配正则关系的匹配器.

    public class RegexMatcher extends TypeSafeMatcher<String> {
        private final String regex;
        public RegexMatcher(final String regex) {
            this.regex = regex;
        }
        @Override
        public void describeTo(final Description description) {
            description.appendText("matches regular expression=`" + regex + "`");
        }
        @Override
        public boolean matchesSafely(final String string) {
            return string.matches(regex);
        }
        // matcher method you can call on this matcher class
        public static RegexMatcher matchesRegex(final String regex) {
            return new RegexMatcher(regex);
        }
    }
    
    测试
    @Test
    public void testRegularExpressionMatcher() throws Exception {
        String s ="aaabbbaaaa";
        assertThat(s, RegexMatcher.matchesRegex("a*b*a*"));
    }
    

    自定义组合匹配器

    为什么要自定义组合匹配器

    Hamcrest有内置的组合匹配器,但是它的可读性太差!

    下面就是一个案例:

    @Test
    public void testCombining() {
        List<Integer> list = new ArrayList<>();
        assertThat(list, both(hasSize(1)).and(contains(42)));
    }
    

    可读性差,无法准确描述错误信息.

    创建自定义组合匹配器

    我们可以继承BaseMatchers类使用它提供对外连接的方法(matches),本身再提供一个添加方法(add).将匹配器链接起来.并保存在集合中.

    public class MatcherCombinator<T> extends BaseMatcher<T> {
        private final List<Matcher<? super T>> matchers = new ArrayList<>();
        private final List<Matcher<? super T>> failedMatchers = new ArrayList<>();
    
        private MatcherCombinator(final Matcher<? super T> matcher) {
            matchers.add(matcher);
        }
    
        public MatcherCombinator<T> and(final Matcher<? super T> matcher) {
            matchers.add(matcher);
            return this;
        }
    
        @Override
        public boolean matches(final Object item) {
            boolean matchesAllMatchers = true;
            for (final Matcher<? super T> matcher : matchers) {
                if (!matcher.matches(item)) {
                    failedMatchers.add(matcher);
                    matchesAllMatchers = false;
                }
            }
            return matchesAllMatchers;
        }
    
        @Override
        public void describeTo(final Description description) {
            description.appendValueList("\n", " " + "and" + "\n", "", matchers);
        }
    
        @Override
        public void describeMismatch(final Object item, final Description description) {
            description.appendText("\n");
            for (Iterator<Matcher<? super T>> iterator = failedMatchers.iterator(); iterator.hasNext();) {
                final Matcher<? super T> matcher = iterator.next();
                description.appendText("Expected: <");
                description.appendDescriptionOf(matcher).appendText(" but ");
                matcher.describeMismatch(item, description);
                if (iterator.hasNext()) {
                    description.appendText(">\n");
                }
            }
        }
    
        public static <LHS> MatcherCombinator<LHS> matches(final Matcher<? super LHS> matcher) {
            return new MatcherCombinator<LHS>(matcher);
        }
    }
    
    测试
    @Test
    public void testCustomCombining() {
        List<Integer> list = new ArrayList<>();
        assertThat(list, MatcherCombinator.matches(hasSize(1)).and(contains(42)));
    }
    

    引用:
    引用1
    引用2
    引用3

    相关文章

      网友评论

          本文标题:单元测试——Hamcrest匹配器框架

          本文链接:https://www.haomeiwen.com/subject/lfjeghtx.html