1. 引言

Hamcrest 不仅提供内置匹配器,还支持创建自定义匹配器。本文将深入探讨如何创建和使用自定义匹配器。想快速了解现有匹配器?可参考这篇指南

2. 自定义匹配器环境配置

要使用 Hamcrest,需在 pom.xml 中添加以下 Maven 依赖

<dependency>
    <groupId>org.hamcrest</groupId>
    <artifactId>java-hamcrest</artifactId>
    <version>2.2</version>
    <scope>test</scope>
</dependency>

最新版本可在 Maven Central 查询。

3. 深入理解 TypeSafeMatcher

开始示例前,必须掌握 TypeSafeMatcher创建自定义匹配器时需继承该类

TypeSafeMatcher 是抽象类,子类必须实现两个方法:

  • matchesSafely(T t):包含核心匹配逻辑
  • describeTo(Description description):自定义匹配失败时的提示信息

注意:**TypeSafeMatcher 是泛型类**,使用时需声明类型参数,即待测试对象的类型。下一节的示例将清晰展示这点。

4. 创建 onlyDigits 匹配器

第一个需求:创建匹配器判断字符串是否仅包含数字。例如:

  • ✅ "123" → 返回 true
  • ❌ "hello1" → 返回 false
  • ❌ "bye" → 返回 false

4.1 匹配器实现

创建继承 TypeSafeMatcher 的类:

public class IsOnlyDigits extends TypeSafeMatcher<String> {
   
    @Override
    protected boolean matchesSafely(String s) {
        // 实现逻辑
    }

    @Override
    public void describeTo(Description description) {
        // 描述信息
    }
}

由于测试对象是字符串,**泛型参数指定为 String**。完整实现:

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");
    }
}

核心逻辑:尝试将字符串转为整数,成功则返回 truedescribeTo 定义了期望描述。

最后添加静态方法,使其与内置匹配器用法一致:

public static Matcher<String> onlyDigits() {
    return new IsOnlyDigits();
}

4.2 匹配器使用

测试用例

@Test
public void givenAString_whenIsOnlyDigits_thenCorrect() {
    String digits = "1234";
    assertThat(digits, onlyDigits());
}

测试通过,因为输入仅含数字。为增强可读性,可用 is 包装匹配器

assertThat(digits, is(onlyDigits()));

若输入 "123ABC",失败信息为:

java.lang.AssertionError: 
Expected: only digits
     but: was "123ABC"

⚠️ describeTo 的描述直接影响错误信息,务必清晰准确。

5. 创建 divisibleBy 匹配器

需求:判断数字能否被指定数整除。此时需存储参数:

public class IsDivisibleBy extends TypeSafeMatcher<Integer> {

    private Integer divider; // 存储除数

    public IsDivisibleBy(Integer divider) {
        this.divider = divider;
    }

    @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);
    }
}

关键点:通过构造函数传入参数,静态方法暴露接口。测试用例:

@Test
public void givenAnEvenInteger_whenDivisibleByTwo_thenCorrect() {
    Integer ten = 10;
    assertThat(ten, is(divisibleBy(2)));
}

@Test
public void givenAnOddInteger_whenNotDivisibleByTwo_thenCorrect() {
    Integer eleven = 11;
    assertThat(eleven, is(not(divisibleBy(2))));
}

简单粗暴,多参数匹配器搞定!

6. 总结

Hamcrest 内置匹配器覆盖了大部分断言场景。当遇到特殊需求时,自定义匹配器是优雅的解决方案——正如本文所示。它们创建简单,用法与内置匹配器完全一致。

完整示例代码见 GitHub 项目


原始标题:Hamcrest Custom Matchers

» 下一篇: JavaFX 介绍