接下来,我们学习一下匹配器Matcher和Junit4中新引入的assertThat断言方法。
1.2 Matcher匹配器和 assertThat
之前我们的环境配置是使用的eclipse自带的JUnit4 Library,它包含junit和hamcrest.core核心包。
我们下面讲解的Matcher匹配器不仅需要core核心包,还需要library包。
[操作2]
所以,得先将环境配置好。
一个一个将下载好的jar包添加进Build Path路径下。
Junit.jar依赖于hamcrest-core.jar
还需要hamcrest-library.jar。
也许有同学疑问,为什么不只将hamcrest-library.jar添加到Build Path路径下?
这是因为我们下载的hamcrest-library.jar版本是和原先JUnit4 Library下的hamcrest-core.jar版本是不一样的,是不能协调工作的。所以要重新导入。
我们导入包之后,一般使用Hamcrest hamcrest-library.jar中提供的匹配器对象,其实JUnit4中也有提供类似的匹配器对象JUnitMatchers。
它提供的方法大部分在hamcrest中都已经有了,所以在后期被弃用了,JUnit更加专注于测试框架本身。
我们后面也会略带提一下JUnitMatchers的使用。
现在,我们开始测试。
[操作3]
新建测试类AssertThatTest。
首先静态导入匹配器对象。在org.hamcrest下。
hamcrest提供了几种匹配器,为了便于理解,我把它分类为一般匹配符、字符串相关匹配符、数值相关匹配符以及集合collection相关匹配符。
我们使用assertThat断言搭配Matcher匹配器对象。
先来学习一般匹配符。
1.2.0.1 ㈠ 一般匹配符
1.2.0.1.1 allOf
allOf匹配符表明如果接下来的所有条件必须都成立测试才通过,相当于“与”(&&)
// allOf
assertThat(10, allOf(greaterThan(8), lessThan(16)));
assertThat断言的参数为(可选的错误提示、测试的真正输出取值以及一个Matcher对象)。
1.2.0.1.2 anyOf
anyOf匹配符表明如果接下来的所有条件只要有一个成立则测试通过,相当于“或”(||)
// anyOf
assertThat(20, anyOf(greaterThan(8), lessThan(16)));
1.2.0.1.3 anything
anything匹配符表明无论什么条件,永远为true。
// anything
assertThat("ok", anything("anything is true"));
1.2.0.1.4 is
is匹配符表明如果前面待测的object等于后面给出的object,则测试通过。
// is
assertThat("i", is("i"));
1.2.0.1.5 not
not匹配符和is匹配符正好相反,表明如果前面待测的object不等于后面给出的object,则测试通过
// not
assertThat("he", is(not("me")));
从上面编写的assertThat断言方式来看,assertThat方法具有很多优点。
1.1.1.1 assertThat优点
l 它可以替代之前我们所罗列的很多assertion语句,包括assertEquals等;
l assertThat 使用了 Hamcrest 的 Matcher 匹配器对象,用户可以使用匹配器对象规定的匹配准则精确的指定一些想设定满足的条件,具有很强的易读性,而且使用起来更加灵活;
l 它的易读性体现在:assertThat 不再像 assertEquals 那样,使用比较难懂的“谓宾主”语法模式(如:assertEquals(3, x);),相反,assertThat 使用了类似于“主谓宾”的易读语法模式(如:assertThat(x,is(3));),使得代码更加直观、易读;
l assertThat方法可以将这些 Matcher 匹配器对象联合起来灵活使用,达到更多目的;
l assertThat方法的错误信息更加易懂、可读且具有描述性。
开发人员可以通过实现 Matcher 接口,定制自己想要的匹配器对象。
特别是,当开发人员发现自己的某些测试代码在不同的测试中重复出现、经常被使用,这时用户就可以自定义匹配器对象,将这些代码绑定在一个断言语句中,从而可以达到减少重复代码并且更加易读的目的。
接下来我们学习一下字符串相关的匹配符。
1.2.0.2 ㈡ 字符串相关的匹配符
1.2.0.2.1 containsString
containsString匹配符表明如果测试的字符串包含子字符串则测试通过。
// containsString
assertThat("tomcat", containsString("cat"));
1.2.0.2.2 endsWith
endsWith匹配符表明如果测试的字符串以子字符串结尾则测试通过。
// startsWith
assertThat("tomcat", startsWith("tom"));
1.2.0.2.3 startsWith
startsWith匹配符表明如果测试的字符串以子字符串开始则测试通过。
// endsWith
assertThat("tomcat", endsWith("at"));
1.2.0.2.4 equalTo
equalTo匹配符表明如果测试的值等于预期的值则测试通过,equalTo可以测试数值之间,字符串之间和对象之间是否相等,相当于Object的equals方法。
// equalTo
assertThat("tomcat", equalTo("tomcat"));
1.2.0.2.5 equalToIgnoringCase
equalToIgnoringCase匹配符表明如果测试的字符串在忽略大小写的情况下等于则测试通过。
// equalToIgnoringCase
assertThat("tomcat", equalToIgnoringCase("TOMCAT"));
1.2.0.2.6 equalToIgnoringWhiteSpace
equalToIgnoringWhiteSpace匹配符表明如果测试的字符串在忽略头尾的任意个空格的情况下等于则测试通过,注意:字符串中的空格不能被忽略。
// equalToIgnoringWhiteSpace
assertThat("tomcat", equalToIgnoringWhiteSpace(" tomcat "));
接下来我们学习数值相关的匹配符。
1.2.0.3 ㈢ 数值相关的匹配符
1.2.0.3.1 closeTo
closeTo匹配符表明如果所测试的浮点型数在一定范围内则通过。如
// closeTo
assertThat(20.0, closeTo(20.0, 0.5));
在20.0±0.5范围之内则测试通过。
这给浮点数之间比较相等,提供了便利的途径。
greaterThan和lessThan在之前也已经演示过。
1.2.0.3.2 greaterThan
greaterThan匹配符表明如果所测试的数值大于指定值则测试通过。
1.2.0.3.3 lessThan
lessThan匹配符表明如果所测试的数值小于指定值则测试通过。
1.2.0.3.4 greaterThanOrEqualTo
greaterThanOrEqualTo匹配符表明如果所测试的数值大于等于指定值则测试通过。
// greaterThanOrEqualTo
assertThat(16.0, greaterThanOrEqualTo(16.0));
1.2.0.3.5 lessThanOrEqualTo
lessThanOrEqualTo匹配符表明如果所测试的数值小于等于指定值则测试通过。
// lessThanOrEqualTo
assertThat(16.0, lessThanOrEqualTo(16.0));
接下来,我们学习集合collection相关的匹配符。
1.2.0.4 ㈣ 集合相关的匹配符
准备工作:
@Before
public void setUp() {
list = new ArrayList<Integer>();
list.add(100);
list.add(10);
map = new HashMap<String, String>();
map.put("key", "value");
}
首先我们来回顾一下“迭代”:
迭代其实我们可以简单地理解为遍历,是一个标准化遍历各类容器里面的所有对象的方法类,它是一个很典型的设计模式。Iterator 模式是用于遍历集合类的标准访问方法。它可以把访问逻辑从不同类型的集合类中抽象出来,从而避免向客户端暴露集合的内部结构。
1.2.0.4.1 hasItem
hasItem匹配器对象表明如果测试的可迭代对象含有指定元素项则测试通过。Java中的集合实现了Iterator 接口的有List一族或者Set一族。它们适用于该方法。
// hasItem - List/Set
assertThat(list, hasItem(100));
1.2.0.4.2 hasEntry
hasEntry匹配器对象表明如果测试的Map对象含有一个与指定键值和元素值对应的Entry项时则测试通过。
// hasEntry - Map
assertThat(map ,hasEntry("key", "value"));
1.2.0.4.3 hasKey
hasKey匹配器对象表明如果测试的Map对象mapObject含有指定键值则测试通过。
// hasKey - Map
assertThat(map ,hasKey("key"));
1.2.0.4.4 hasValue
hasValue匹配器对象表明如果测试的Map对象含有指定元素值则测试通过。
// hasValue - Map
assertThat(map ,hasValue("value"));
接下来我们了解一下JunitMatchers的用法。
1.2.0.5 JunitMatchers的用法
在继续编写之前,先要将导入的包指定清楚。
因为JunitMatchers会跟hamcrest中提供的方法名相同,会产生一些冲突。
[操作3]
快捷键导入指定的类。
然后,我们再导入JunitMatchers。
在之后的方法使用前,明确是JunitMatchers类下的。
1.2.0.5.1 both
both和之前的allOf匹配符用法相似。我们编写一个字符串包含子串的例子。
@Test
public void testAssertThatBothContainsString() {
assertThat("albumen", JUnitMatchers.both(JUnitMatchers.containsString("a")).and(JUnitMatchers.containsString("b")));
}
采用的是链式结构编写。
1.2.0.5.2 hasItems
hasItems和之前的hasItem类似,Hamcrest中也有hasItems。
@Test
public void testAssertThathasItemsContainsString() {
assertThat(Arrays.asList("one", "two", "three"), JUnitMatchers.hasItems("one","two"));
}
JunitMatchers大部分方法被弃用了。所有不用过多的了解。
我们再来看一个everyItem方法吧,这个作用于集合中每个元素。
1.2.0.5.3 everyItem
everyItem匹配符用来对集合中每个元素比较。例如,判断集合中每个元素都包含“n”。
@Test
public void testAssertThatEveryItemContainsString() {
assertThat(Arrays.asList("fun", "ban", "net"), JUnitMatchers.everyItem(JUnitMatchers.containsString("n")));
}