1. Introduction
1.介绍
As well as built-in matchers, Hamcrest also provides support for creating custom matchers.
除了内置的匹配器,Hamcrest还提供对创建自定义匹配器的支持。
In this tutorial, we’ll take a closer look at how to create and use them. To get a sneak peek on the available matchers, refer to this article.
在本教程中,我们将仔细研究如何创建和使用它们。要了解可用的匹配器,请参考这篇文章。
2. Custom Matchers Setup
2.自定义匹配器设置
To get Hamcrest, we need to add the following Maven dependency to our pom.xml:
为了获得Hamcrest,我们需要在我们的pom.xml中添加以下Maven依赖项。
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>java-hamcrest</artifactId>
<version>2.0.0.0</version>
<scope>test</scope>
</dependency>
The latest Hamcrest version can be found on Maven Central.
最新的Hamcrest版本可以在Maven Central上找到。
3. Introducing TypeSafeMatcher
3.介绍TypeSafeMatcher
Before starting with our examples, it’s important to understand the class TypeSafeMatcher. We’ll have to extend this class to create a matcher of our own.
在开始我们的例子之前,了解TypeSafeMatcher类是很重要的。 我们必须扩展这个类来创建一个我们自己的匹配器。
TypeSafeMatcher is an abstract class, so all subclasses have to implement the following methods:
TypeSafeMatcher是一个抽象类,所以所有子类都必须实现以下方法。
- matchesSafely(T t): contains our matching logic
- describeTo(Description description): customizes the message the client will get when our matching logic is not fulfilled
As we may see in the first method, TypeSafeMatcher is parametrized, so we’ll have to declare a type when we use it. That will be the type of the object we’re testing.
正如我们在第一个方法中可能看到的,TypeSafeMatcher是参数化的,所以当我们使用它时,我们必须声明一个类型。
Let’s make this clearer by looking at our first example in the next section.
让我们在下一节中看看我们的第一个例子,使之更加清晰。
4. Creating the onlyDigits Matcher
4.创建onlyDigits匹配器
For our first use case, we’ll create a matcher that returns true if a certain String contains only digits.
对于我们的第一个用例,我们将创建一个匹配器,如果某个字符串只包含数字,则返回true。
So, onlyDigits applied to “123” should return true while “hello1” and “bye” should return false.
因此,应用于 “123 “的onlyDigits应该返回true,而”hello1“和”bye“应该返回false。
Let’s get started!
让我们开始吧!
4.1. Matcher Creation
4.1.创建匹配器
To start with our matcher, we’ll create a class that extends TypeSafeMatcher:
为了开始我们的匹配器,我们将创建一个扩展TypeSafeMatcher的类。
public class IsOnlyDigits extends TypeSafeMatcher<String> {
@Override
protected boolean matchesSafely(String s) {
// ...
}
@Override
public void describeTo(Description description) {
// ...
}
}
Please note that as the object we’ll test is a text, we’re parametrizing our subclass of TypeSafeMatcher with the class String.
请注意,由于我们要测试的对象是一个文本,我们要用String类来参数化我们的TypeSafeMatcher子类。
Now we’re ready to add our implementation:
现在我们准备添加我们的实现。
public class IsOnlyDigits extends TypeSafeMatcher<String> {
@Override
protected boolean matchesSafely(String s) {
try {
Integer.parseInt(s);
return true;
} catch (NumberFormatException nfe){
return false;
}
}
@Override
public void describeTo(Description description) {
description.appendText("only digits");
}
}
As we can see, matchesSafey is trying to parse our input String into an Integer. If it succeeds, it returns true. If it fails, it returns false. It responds successfully to our use case.
我们可以看到,matchesSafey正试图将我们的输入String解析为Integer。如果它成功了,它就会返回true。如果它失败了,它将返回false。它成功地回应了我们的用例。
On the other side, describeTo is attaching a text that represents our expectations. We’ll see how this shows next when we use our matcher.
在另一边,describeTo 是附加一个代表我们期望的文本。接下来我们将看到,当我们使用我们的匹配器时,这一点是如何显示的。
We only need one more thing to complete our matcher: a static method to access it, so it behaves as the rest of the built-in matchers.
我们还需要一个东西来完成我们的匹配器:一个静态方法来访问它,所以它的行为和其他内置匹配器一样。
So, we’ll add something like this:
因此,我们将添加这样的内容。
public static Matcher<String> onlyDigits() {
return new IsOnlyDigits();
}
And we’re done! Let’s see how to use this matcher in the next section.
然后我们就完成了!让我们在下一节看看如何使用这个匹配器。
4.2. Matcher Usage
4.2.匹配器的使用
To use our brand new matcher, we’ll create a test:
为了使用我们全新的匹配器,我们将创建一个测试。
@Test
public void givenAString_whenIsOnlyDigits_thenCorrect() {
String digits = "1234";
assertThat(digits, onlyDigits());
}
And that’s it. This test will pass because the input String contains only digits. Remember that, to make it a little more legible, we can use the matcher is that acts as a wrapper over any other matcher:
就这样了。这个测试将通过,因为输入的String只包含数字。请记住,为了使其更加清晰,我们可以使用匹配器is,它可以作为任何其他匹配器的包装器。
assertThat(digits, is(onlyDigits()));
Finally, if we ran the same test but with the input “123ABC”, the output message would be:
最后,如果我们运行同样的测试,但输入 “123ABC”,输出信息将是。
java.lang.AssertionError:
Expected: only digits
but: was "123ABC"
This is where we see the text that we appended to the describeTo method. As we may have noticed, it’s important to create a proper description of what’s expected in the test.
在这里我们可以看到我们附加到describeTo方法的文本。正如我们可能已经注意到的,对测试中预期的内容进行适当的描述是很重要的。
5. divisibleBy
5.divisibleBy
So, what if we wanted to create a matcher that defines if a number is divisible by another number? For that scenario, we’ll have to store one of the parameters somewhere.
那么,如果我们想创建一个匹配器来定义一个数字是否能被另一个数字整除呢?对于这种情况,我们必须在某个地方存储一个参数。
Let’s see how we can do that:
让我们看看我们如何能够做到这一点。
public class IsDivisibleBy extends TypeSafeMatcher<Integer> {
private Integer divider;
// constructors
@Override
protected boolean matchesSafely(Integer dividend) {
if (divider == 0) {
return false;
}
return ((dividend % divider) == 0);
}
@Override
public void describeTo(Description description) {
description.appendText("divisible by " + divider);
}
public static Matcher<Integer> divisibleBy(Integer divider) {
return new IsDivisibleBy(divider);
}
}
Simple enough, we just added a new attribute to our class and assigned it during construction. Then, we just passed it as a parameter to our static method:
很简单,我们只是给我们的类添加了一个新的属性,并在构造过程中分配了它。然后,我们就把它作为参数传给我们的静态方法。
@Test
public void givenAnEvenInteger_whenDivisibleByTwo_thenCorrect() {
Integer ten = 10;
Integer two = 2;
assertThat(ten,is(divisibleBy(two)));
}
@Test
public void givenAnOddInteger_whenNotDivisibleByTwo_thenCorrect() {
Integer eleven = 11;
Integer two = 2;
assertThat(eleven,is(not(divisibleBy(two))));
}
And that’s it! We already have our matcher using more than one input!
这就是了!我们已经有了一个使用多个输入的匹配器。我们已经有了使用一个以上的输入的匹配器
6. Conclusion
6.结论
Hamcrest provides matchers that cover most use cases a developer usually has to deal with when creating assertions.
Hamcrest提供的匹配器涵盖了开发人员在创建断言时通常需要处理的大多数使用情况。
What’s more, if any specific case isn’t covered, Hamcrest also gives support to create custom matchers to be used under specific scenarios – as we’ve explored here. They’re simple to create, and they are used exactly like the ones included in the library.
更重要的是,如果没有涵盖任何特定的情况,Hamcrest还支持创建自定义匹配器,以便在特定的情况下使用–正如我们在这里探讨的那样。它们的创建很简单,而且它们的使用与库中包含的匹配器完全一样。
To get the complete implementation of this examples, please refer to the GitHub Project.
要获得这个例子的完整实现,请参考GitHub项目。