Testing with Hamcrest – 用Hamcrest测试

最后修改: 2016年 7月 3日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

Hamcrest is the well-known framework used for unit testing in the Java ecosystem. It’s bundled in JUnit and simply put, it uses existing predicates – called matcher classes – for making assertions.

Hamcrest 是Java生态系统中用于单元测试的著名框架。它被捆绑在JUnit中,简单地说,它使用现有的谓词–称为匹配器类–来做断言。

In this tutorial, we will explore the Hamcrest API and learn how to take advantage of it to write neater and more intuitive unit tests for our software.

在本教程中,我们将探索Hamcrest API并学习如何利用它为我们的软件编写更整洁、更直观的单元测试。

2. Hamcrest Setup

2.Hamcrest设置

We can use Hamcrest with maven by adding the following dependency to our pom.xml file:

我们可以通过在pom.xml文件中添加以下依赖关系来使用Hamcrest与maven。

<dependency>
    <groupId>org.hamcrest</groupId>
    <artifactId>hamcrest-all</artifactId>
    <version>1.3</version>
</dependency>

The latest version of this library can always be found here.

这个库的最新版本总是可以在这里找到。

3. An Example Test

3.一个测试实例

Hamcrest is commonly used with junit and other testing frameworks for making assertions. Specifically, instead of using junit‘s numerous assert methods, we only use the API’s single assertThat statement with appropriate matchers.

Hamcrest通常与junit和其他测试框架一起用于做断言。具体来说,我们不使用junit的众多assert方法,而只使用API的单一assertThat语句和适当的匹配器。

Let’s look at an example that tests two Strings for equality regardless of case. This should give us a clear idea about how Hamcrest fits in to a testing method:

让我们看一个例子,测试两个Strings是否相等,不考虑大小写。这应该让我们清楚地了解到Hamcrest如何适合测试方法。

public class StringMatcherTest {
    
    @Test
    public void given2Strings_whenEqual_thenCorrect() {
        String a = "foo";
        String b = "FOO";
        assertThat(a, equalToIgnoringCase(b));
    }
}

In the following sections we shall take a look at several other common matchers Hamcrest offers.

在下面的章节中,我们将看一下Hamcrest提供的其他几个常见的匹配器。

4. The Object Matcher

4.对象匹配器

Hamcrest provides matchers for making assertions on arbitrary Java objects.

Hamcrest提供匹配器,用于对任意的Java对象进行断言。

To assert that the toString method of an Object returns a specified String:

断言一个ObjecttoString方法返回一个指定的String

@Test
public void givenBean_whenToStringReturnsRequiredString_thenCorrect(){
    Person person=new Person("Barrack", "Washington");
    String str=person.toString();
    assertThat(person,hasToString(str));
}

We can also check that one class is a sub-class of another:

我们还可以检查一个类是否是另一个类的子类。

@Test
public void given2Classes_whenOneInheritsFromOther_thenCorrect(){
        assertThat(Cat.class,typeCompatibleWith(Animal.class));
    }
}

5. The Bean Matcher

5.Bean匹配器

We can use Hamcrest‘s Bean matcher to inspect properties of a Java bean.

我们可以使用Hamcrest的Bean匹配器来检查一个Java Bean的属性。

Assume the following Person bean:

假设以下PersonBean。

public class Person {
    String name;
    String address;

    public Person(String personName, String personAddress) {
        name = personName;
        address = personAddress;
    }
}

We can check if the bean has the property, name like so:

我们可以像这样检查Bean是否有属性,name

@Test
public void givenBean_whenHasValue_thenCorrect() {
    Person person = new Person("Baeldung", 25);
    assertThat(person, hasProperty("name"));
}

We can also check if Person has the address property, initialized to New York:

我们还可以检查Person是否有address属性,初始化为纽约。

@Test
public void givenBean_whenHasCorrectValue_thenCorrect() {
    Person person = new Person("Baeldung", "New York");
    assertThat(person, hasProperty("address", equalTo("New York")));
}

We can as well check if two Person objects are constructed with the same values:

我们也可以检查两个Person对象是否以相同的值构建。

@Test
public void given2Beans_whenHavingSameValues_thenCorrect() {
    Person person1 = new Person("Baeldung", "New York");
    Person person2 = new Person("Baeldung", "New York");
    assertThat(person1, samePropertyValuesAs(person2));
}

6. The Collection Matcher

6.Collection匹配器

Hamcrest provides matchers for inspecting Collections.

Hamcrest提供了用于检查Collections的匹配器。

Simple check to find out if a Collection is empty:

简单地检查出一个Collection是否为空。

@Test
public void givenCollection_whenEmpty_thenCorrect() {
    List<String> emptyList = new ArrayList<>();
    assertThat(emptyList, empty());
}

To check the size of a Collection:

要检查一个集合的大小:

@Test
public void givenAList_whenChecksSize_thenCorrect() {
    List<String> hamcrestMatchers = Arrays.asList(
      "collections", "beans", "text", "number");
    assertThat(hamcrestMatchers, hasSize(4));
}

We can also use it to assert that an array has a required size:

我们也可以用它来断言一个数组有一个所需的大小。

@Test
public void givenArray_whenChecksSize_thenCorrect() {
    String[] hamcrestMatchers = { "collections", "beans", "text", "number" };
    assertThat(hamcrestMatchers, arrayWithSize(4));
}

To check if a Collection contains given members, regardless of order:

检查一个Collection是否包含给定的成员,无论顺序如何。

@Test
public void givenAListAndValues_whenChecksListForGivenValues_thenCorrect() {
    List<String> hamcrestMatchers = Arrays.asList(
      "collections", "beans", "text", "number");
    assertThat(hamcrestMatchers,
    containsInAnyOrder("beans", "text", "collections", "number"));
}

To further assert that the Collection members are in given order:

为了进一步断言Collection成员是按给定顺序排列的。

@Test
public void givenAListAndValues_whenChecksListForGivenValuesWithOrder_thenCorrect() {
    List<String> hamcrestMatchers = Arrays.asList(
      "collections", "beans", "text", "number");
    assertThat(hamcrestMatchers,
    contains("collections", "beans", "text", "number"));
}

To check if an array has a single given element:

检查一个数组是否有单一的给定元素。

@Test
public void givenArrayAndValue_whenValueFoundInArray_thenCorrect() {
    String[] hamcrestMatchers = { "collections", "beans", "text", "number" };
    assertThat(hamcrestMatchers, hasItemInArray("text"));
}

We can also use an alternative matcher for the same test:

我们也可以使用另一种匹配器进行同样的测试。

@Test
public void givenValueAndArray_whenValueIsOneOfArrayElements_thenCorrect() {
    String[] hamcrestMatchers = { "collections", "beans", "text", "number" };
    assertThat("text", isOneOf(hamcrestMatchers));
}

Or still we can do the same with a different matcher like so:

或者我们仍然可以用一个不同的匹配器来做同样的事情,就像这样。

@Test
public void givenValueAndArray_whenValueFoundInArray_thenCorrect() {
    String[] array = new String[] { "collections", "beans", "text",
      "number" };
    assertThat("beans", isIn(array));
}

We can also check if the array contains given elements regardless of order:

我们也可以检查数组是否包含给定的元素而不考虑顺序。

@Test
public void givenArrayAndValues_whenValuesFoundInArray_thenCorrect() {
    String[] hamcrestMatchers = { "collections", "beans", "text", "number" };
      assertThat(hamcrestMatchers,
    arrayContainingInAnyOrder("beans", "collections", "number",
      "text"));
}

To check if the array contains given elements but in the given order:

检查数组是否包含给定的元素,但以给定的顺序排列。

@Test
public void givenArrayAndValues_whenValuesFoundInArrayInOrder_thenCorrect() {
    String[] hamcrestMatchers = { "collections", "beans", "text", "number" };
    assertThat(hamcrestMatchers,
    arrayContaining("collections", "beans", "text", "number"));
}

When our Collection is a Map, we can use the following matchers in these respective functions:

当我们的Collection是一个Map,时,我们可以在这些相应的函数中使用以下匹配器。

To check if it contains a given key:

要检查它是否包含一个给定的键。

@Test
public void givenMapAndKey_whenKeyFoundInMap_thenCorrect() {
    Map<String, String> map = new HashMap<>();
    map.put("blogname", "baeldung");
    assertThat(map, hasKey("blogname"));
}

and a given value:

和一个给定值。

@Test
public void givenMapAndValue_whenValueFoundInMap_thenCorrect() {
    Map<String, String> map = new HashMap<>();
    map.put("blogname", "baeldung");
    assertThat(map, hasValue("baeldung"));
}

and finally a given entry (key, value):

最后是一个给定的条目(键,值)。

@Test
public void givenMapAndEntry_whenEntryFoundInMap_thenCorrect() {
    Map<String, String> map = new HashMap<>();
    map.put("blogname", "baeldung");
    assertThat(map, hasEntry("blogname", "baeldung"));
}

7. The Number Matcher

7.数字匹配器

The Number matchers are used to perform assertions on variables of the Number class.

Number匹配器用于对Number类的变量执行断言。

To check greaterThan condition:

检查greaterThan条件。

@Test
public void givenAnInteger_whenGreaterThan0_thenCorrect() {
    assertThat(1, greaterThan(0));
}

To check greaterThan or equalTo condition:

检查greaterThanequalTo条件。

@Test
public void givenAnInteger_whenGreaterThanOrEqTo5_thenCorrect() {
    assertThat(5, greaterThanOrEqualTo(5));
}

To check lessThan condition:

要检查lessThan条件。

@Test
public void givenAnInteger_whenLessThan0_thenCorrect() {
    assertThat(-1, lessThan(0));
}

To check lessThan or equalTo condition:

要检查lessThanequalTo条件。

@Test
public void givenAnInteger_whenLessThanOrEqTo5_thenCorrect() {
    assertThat(-1, lessThanOrEqualTo(5));
}

To check closeTo condition:

要检查closeTo条件。

@Test
public void givenADouble_whenCloseTo_thenCorrect() {
    assertThat(1.2, closeTo(1, 0.5));
}

Let’s pay close attention to the last matcher, closeTo. The first argument, the operand, is the one to which the target is compared and the second argument is the allowable deviation from the operand . This means that if the target is operand+deviation or operand-deviation, then the test will pass.

让我们密切关注最后一个匹配器,closeTo.第一个参数,操作数,是目标被比较的对象,第二个参数是允许与操作数的偏差这意味着,如果目标是操作数+偏差或操作数-偏差,那么测试将通过。

8. The Text Matcher

8.文本匹配器

Assertion on Strings is made easier, neater and more intuitive with Hamcrest‘s text matchers. We are going to take a look at them in this section.

有了Hamcrest的文本匹配器,对Strings的断言变得更容易、更整洁和更直观。我们将在本节中对其进行考察。

To check if a String is empty:

检查一个String是否为空。

@Test
public void givenString_whenEmpty_thenCorrect() {
    String str = "";
    assertThat(str, isEmptyString());
}

To check if a String is empty or null:

检查一个String是否为空或null

@Test
public void givenString_whenEmptyOrNull_thenCorrect() {
    String str = null;
    assertThat(str, isEmptyOrNullString());
}

To check for equality of two Strings while ignoring white space:

检查两个Strings是否相等,同时忽略空白处。

@Test
public void given2Strings_whenEqualRegardlessWhiteSpace_thenCorrect() {
    String str1 = "text";
    String str2 = " text ";
    assertThat(str1, equalToIgnoringWhiteSpace(str2));
}

We can also check for the presence of one or more sub-strings in a given String in a given order:

我们还可以检查一个给定的String中是否有一个或多个子字符串按给定的顺序存在。

@Test
public void givenString_whenContainsGivenSubstring_thenCorrect() {
    String str = "calligraphy";
    assertThat(str, stringContainsInOrder(Arrays.asList("call", "graph")));
}

Finally, we can check for equality of two Strings regardless of case:

最后,我们可以检查两个Strings是否相等,而不考虑具体情况。

@Test
 public void given2Strings_whenEqual_thenCorrect() {
    String a = "foo";
    String b = "FOO";
    assertThat(a, equalToIgnoringCase(b));
}

9. The Core API

9.核心API

The Hamcrest core API is to be used by third-party framework providers. However, it offers us some great constructs to make our unit tests more readable and also some core matchers that can be used just as easily.

Hamcrest核心API要由第三方框架供应商使用。然而,它为我们提供了一些很好的结构,使我们的单元测试更加可读,也提供了一些核心匹配器,可以同样方便地使用。

Readability with the is construct on a matcher:

在匹配器上使用is结构的可读性。

@Test
public void given2Strings_whenIsEqualRegardlessWhiteSpace_thenCorrect() {
    String str1 = "text";
    String str2 = " text ";
    assertThat(str1, is(equalToIgnoringWhiteSpace(str2)));
}

The is construct on a simple data type:

在一个简单的数据类型上的is结构。

@Test
public void given2Strings_whenIsEqual_thenCorrect() {
    String str1 = "text";
    String str2 = "text";
    assertThat(str1, is(str2));
}

Negation with the not construct on a matcher:

在匹配器上使用not结构进行否定。

@Test
public void given2Strings_whenIsNotEqualRegardlessWhiteSpace_thenCorrect() {
    String str1 = "text";
    String str2 = " texts ";
    assertThat(str1, not(equalToIgnoringWhiteSpace(str2)));
}

The not construct on a simple data type:

简单数据类型上的not结构。

@Test
public void given2Strings_whenNotEqual_thenCorrect() {
    String str1 = "text";
    String str2 = "texts";
    assertThat(str1, not(str2));
}

Check if a String contains a given sub-string:

检查一个String是否包含一个给定的子串。

@Test
public void givenAStrings_whenContainsAnotherGivenString_thenCorrect() {
    String str1 = "calligraphy";
    String str2 = "call";
    assertThat(str1, containsString(str2));
}

Check if a String starts with given sub-string:

检查一个字符串是否以给定的子字符串开始。

@Test
public void givenAString_whenStartsWithAnotherGivenString_thenCorrect() {
    String str1 = "calligraphy";
    String str2 = "call";
    assertThat(str1, startsWith(str2));
}

Check if a String ends with given sub-string:

检查一个字符串是否以给定的子字符串结束。

@Test
public void givenAString_whenEndsWithAnotherGivenString_thenCorrect() {
    String str1 = "calligraphy";
    String str2 = "phy";
    assertThat(str1, endsWith(str2));
}

Check if two Objects are of the same instance:

检查两个Objects是否属于同一个实例。

@Test
public void given2Objects_whenSameInstance_thenCorrect() {
    Cat cat=new Cat();
    assertThat(cat, sameInstance(cat));
}

Check if an Object is an instance of a given class:

检查一个Object是否是一个给定类的实例。

@Test
public void givenAnObject_whenInstanceOfGivenClass_thenCorrect() {
    Cat cat=new Cat();
    assertThat(cat, instanceOf(Cat.class));
}

Check if all members of a Collection meet a condition:

检查一个集合的所有成员是否满足一个条件。

@Test
public void givenList_whenEachElementGreaterThan0_thenCorrect() {
    List<Integer> list = Arrays.asList(1, 2, 3);
    int baseCase = 0;
    assertThat(list, everyItem(greaterThan(baseCase)));
}

Check that a String is not null:

检查一个String是不是null

@Test
public void givenString_whenNotNull_thenCorrect() {
    String str = "notnull";
    assertThat(str, notNullValue());
}

Chain conditions together, test passes when target meets any of the conditions, similar to logical OR:

将条件连在一起,当目标满足任何条件时,测试通过,类似于逻辑OR。

@Test
public void givenString_whenMeetsAnyOfGivenConditions_thenCorrect() {
    String str = "calligraphy";
    String start = "call";
    String end = "foo";
    assertThat(str, anyOf(startsWith(start), containsString(end)));
}

Chain conditions together, test passes only when target meets all conditions, similar to logical AND:

将条件连在一起,只有当目标满足所有条件时,测试才通过,类似于逻辑和。

@Test
public void givenString_whenMeetsAllOfGivenConditions_thenCorrect() {
    String str = "calligraphy";
    String start = "call";
    String end = "phy";
    assertThat(str, allOf(startsWith(start), endsWith(end)));
}

10. A Custom Matcher

10.一个自定义匹配器

We can define our own matcher by extending TypeSafeMatcher. In this section, we will create a custom matcher which allows a test to pass only when the target is a positive integer.

我们可以通过扩展TypeSafeMatcher来定义我们自己的匹配器。在本节中,我们将创建一个自定义匹配器,该匹配器只允许在目标是正整数时通过测试。

public class IsPositiveInteger extends TypeSafeMatcher<Integer> {

    public void describeTo(Description description) {
        description.appendText("a positive integer");
    }

    @Factory
    public static Matcher<Integer> isAPositiveInteger() {
        return new IsPositiveInteger();
    }

    @Override
    protected boolean matchesSafely(Integer integer) {
        return integer > 0;
    }

}

We need only to implement the matchSafely method which checks that the target is indeed a positive integer and the describeTo method which produces a failure message in case the test does not pass.

我们只需要实现matchSafely方法和describeTo方法,前者检查目标是否确实是一个正整数,后者在测试未通过的情况下产生一个失败信息。

Here is a test that uses our new custom matcher:

下面是一个使用我们新的自定义匹配器的测试。

@Test
public void givenInteger_whenAPositiveValue_thenCorrect() {
    int num = 1;
    assertThat(num, isAPositiveInteger());
}

and here is a failure message we get since we have passed in a non-positive integer:

下面是我们得到的失败信息,因为我们传入了一个非正的整数。

java.lang.AssertionError: Expected: a positive integer but: was <-1>

11. Conclusion

11.结语

In this tutorial, we have explored the Hamcrest API and learnt how we can write better and more maintainable unit tests with it.

在本教程中,我们已经探索了Hamcrest API,并学习了如何用它编写更好、更可维护的单元测试。

The full implementation of all these examples and code snippets can be found in my Hamcrest github project.

所有这些例子和代码片段的完整实现可以在我的Hamcrest github项目中找到。