1. 概述

本文带你快速掌握 Hamcrest 框架中 CoreMatchers 类的核心用法。Hamcrest 是 Java 中非常流行的断言库,它的核心理念是让测试断言语句读起来更像自然语言,提升测试代码的可读性。

✅ 举个例子:
assertThat(result, is(equalTo("success")))
比传统的 assertEquals("success", result) 更具表达力,逻辑更清晰。

如果你写单元测试还在用一堆 assertTrueassertEquals,那 Hamcrest 真的值得你花点时间了解下,尤其在复杂对象或集合校验时,优势明显。


2. Hamcrest 环境搭建

使用 Maven 的项目,只需在 pom.xml 中引入以下依赖:

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

📌 最新版本可前往 Maven Central 查看。

⚠️ 注意:虽然 JUnit 4 自带部分 Hamcrest 功能,但建议显式引入 java-hamcrest 以获得完整功能支持,避免版本冲突或功能缺失。


3. 常用核心匹配器

3.1 is(T)is(Matcher<T>)

is() 是最常用的“装饰器”方法,它本身不执行判断,而是让语句更自然。

  • is(T value):检查目标是否等于指定值
  • is(Matcher<T>):包装另一个 matcher,增强表达力
String testString = "hamcrest core";

assertThat(testString, is("hamcrest core"));
assertThat(testString, is(equalTo("hamcrest core")));

✅ 实际上 is("xxx") 内部就是调用 equalTo("xxx"),所以两者等价。但 is() 让语句更接近“人话”。


3.2 equalTo(T)

用于判断两个对象是否相等(基于 equals() 方法)。

常与 is() 搭配使用,形成“主谓宾”结构:

String actualString = "equalTo match";
List<String> actualList = Arrays.asList("equalTo", "match");

assertThat(actualString, is(equalTo("equalTo match")));
assertThat(actualList, is(equalTo(Arrays.asList("equalTo", "match"))));

⚠️ 注意:equalTo() 会进行类型检查。如果你希望忽略静态类型(比如 Object 类型对比),可以用:

Object original = 100;
assertThat(original, equalToObject(100)); // 不强制类型一致

但日常使用 equalTo() 足够,类型安全更重要。


3.3 not(T)not(Matcher<T>)

用于否定判断,即“不等于”或“不满足某个条件”。

String testString = "troy kingdom";

assertThat(testString, not("german kingdom"));
assertThat(testString, is(not(equalTo("german kingdom"))));
assertThat(testString, is(not(instanceOf(Integer.class))));

✅ 推荐使用 is(not(...)) 风格,语义清晰,可读性强。


3.4 nullValue()nullValue(Class<T>)

判断目标是否为 null

Integer nullObject = null;

assertThat(nullObject, is(nullValue()));
assertThat(nullObject, is(nullValue(Integer.class)));

后者可以指定类型,但实际效果与前者基本一致,多用于强调类型上下文。


3.5 notNullValue()notNullValue(Class<T>)

判断目标是否非 null,是 is(not(nullValue())) 的简洁写法。

Integer testNumber = 123;

assertThat(testNumber, is(notNullValue()));
assertThat(testNumber, is(notNullValue(Integer.class)));

✅ 日常断言对象非空时,直接用 notNullValue() 更简洁。


3.6 instanceOf(Class<?>)

判断对象是否是某个类的实例(包括子类),内部调用的是 Class.isInstance()

assertThat("instanceOf example", is(instanceOf(String.class)));

⚠️ 注意:它允许继承关系,instanceOf(CharSequence.class) 也能匹配 String


3.7 isA(Class<T>)

instanceOf() 的语法糖,功能完全一致,但读起来更像自然语言。

assertThat("Drogon is biggest dragon", isA(String.class));

✅ 推荐在测试中使用 isA,语句更流畅,比如 isA(List.class) 读作“是一个 List”,很顺。


3.8 sameInstance()

判断两个引用是否指向同一个对象实例(即内存地址相同),基于 == 比较。

String string1 = "Viseron";
String string2 = string1;

assertThat(string1, is(sameInstance(string2)));

⚠️ 区别于 equalTo()sameInstance() 是引用比较,不是值比较。字符串常量池等场景下容易踩坑。


3.9 any(Class<T>)

判断对象是否属于指定类型,常用于泛型匹配或 Mockito 参数捕获。

assertThat("test string", is(any(String.class)));
assertThat("test string", is(any(Object.class)));

✅ 在 mock 测试中非常实用,比如 when(service.process(any(User.class))).thenReturn(...)


3.10 allOf()anyOf()

  • allOf():所有条件都必须满足(逻辑与)
  • anyOf():满足任意一个条件即可(逻辑或)
String testString = "Achilles is powerful";
assertThat(testString, allOf(
    startsWith("Achi"),
    endsWith("ul"),
    containsString("Achilles")
));
String testString = "Hector killed Achilles";
assertThat(testString, anyOf(
    startsWith("Hec"),
    containsString("baeldung")
));

✅ 组合断言利器,避免写多个 assertThat


3.11 hasItem()hasItems()

用于集合(Iterable)中是否包含指定元素。

List<String> list = Arrays.asList("java", "spring", "baeldung");

assertThat(list, hasItem("java"));
assertThat(list, hasItem(isA(String.class)));

批量判断多个元素是否存在:

assertThat(list, hasItems("java", "baeldung"));
assertThat(list, hasItems(isA(String.class), endsWith("ing")));

✅ 集合断言中最常用的 matcher 之一,比 assertTrue(list.contains(...)) 更优雅。


3.12 both()either()

  • both().and():两个条件都满足
  • either().or():满足其一即可
String testString = "daenerys targaryen";
assertThat(testString, both(startsWith("daene")).and(containsString("yen")));
assertThat(testString, either(startsWith("tar")).or(containsString("targaryen")));

✅ 语义清晰,适合简单逻辑组合,比 allOf/anyOf 更贴近自然语言。


4. 字符串匹配

Hamcrest 提供了丰富的字符串断言工具,支持忽略大小写:

包含判断

String testString = "Rhaegar Targaryen";
assertThat(testString, containsString("aegar"));
assertThat(testString, containsStringIgnoringCase("AEGAR"));

开头判断

assertThat(testString, startsWith("Rhae"));
assertThat(testString, startsWithIgnoringCase("rhae"));

结尾判断

assertThat(testString, endsWith("aryen"));
assertThat(testString, endsWithIgnoringCase("ARYEN"));

✅ 这些 matcher 在校验日志、API 响应、文本处理时非常实用,避免手动 indexOf 或正则。


5. 总结

本文系统梳理了 Hamcrest CoreMatchers 中最常用的核心匹配器,涵盖:

  • 基础值比较(equalTo, is, not
  • 空值判断(nullValue, notNullValue
  • 类型判断(instanceOf, isA, any
  • 引用比较(sameInstance
  • 逻辑组合(allOf, anyOf, both, either
  • 集合与字符串专用 matcher

📌 推荐在项目中逐步引入 Hamcrest,尤其是在复杂的 DTO、集合、条件断言场景下,能显著提升测试代码的可读性维护性

所有示例代码已托管至 GitHub:https://github.com/example-java/tutorials/tree/master/testing-modules/hamcrest


原始标题:Hamcrest Common Core Matchers