1. 概述
本文将介绍 JSONAssert 库——一个专注于理解 JSON 数据并基于此编写复杂 JUnit 测试的实用工具。
2. Maven 依赖
首先添加 Maven 依赖:
<dependency>
<groupId>org.skyscreamer</groupId>
<artifactId>jsonassert</artifactId>
<version>1.5.0</version>
</dependency>
最新版本可在 Maven 中央仓库 查询。
3. 处理简单 JSON 数据
3.1. 使用 LENIENT 模式
先从简单的 JSON 字符串比较开始:
String actual = "{id:123, name:\"John\"}";
JSONAssert.assertEquals(
"{id:123,name:\"John\"}", actual, JSONCompareMode.LENIENT);
测试会通过,因为预期和实际 JSON 字符串完全一致。
LENIENT 模式的特点是:即使实际 JSON 包含额外字段,测试仍然通过:
String actual = "{id:123, name:\"John\", zip:\"33025\"}";
JSONAssert.assertEquals(
"{id:123,name:\"John\"}", actual, JSONCompareMode.LENIENT);
实际变量多了 zip
字段,但测试依然通过。✅
这种设计在开发中很实用——API 可以扩展返回新字段,而不会破坏现有测试。
3.2. 使用 STRICT 模式
切换到 STRICT 模式会改变上述行为:
String actual = "{id:123,name:\"John\"}";
JSONAssert.assertNotEquals(
"{name:\"John\"}", actual, JSONCompareMode.STRICT);
注意这里使用了 assertNotEquals()
。❌
3.3. 用布尔值替代 JSONCompareMode
比较模式也可通过布尔值参数指定(false=LENIENT
, true=STRICT
):
String actual = "{id:123,name:\"John\",zip:\"33025\"}";
JSONAssert.assertEquals(
"{id:123,name:\"John\"}", actual, JSONCompareMode.LENIENT);
JSONAssert.assertEquals(
"{id:123,name:\"John\"}", actual, false);
actual = "{id:123,name:\"John\"}";
JSONAssert.assertNotEquals(
"{name:\"John\"}", actual, JSONCompareMode.STRICT);
JSONAssert.assertNotEquals(
"{name:\"John\"}", actual, true);
3.4. 逻辑比较
JSONAssert 执行的是逻辑比较,这意味着 JSON 对象的字段顺序不影响结果:
String result = "{id:1,name:\"John\"}";
JSONAssert.assertEquals(
"{name:\"John\",id:1}", result, JSONCompareMode.STRICT);
JSONAssert.assertEquals(
"{name:\"John\",id:1}", result, JSONCompareMode.LENIENT);
无论严格还是宽松模式,测试都会通过。✅
另一个逻辑比较的例子是不同类型的相同值:
JSONObject expected = new JSONObject();
JSONObject actual = new JSONObject();
expected.put("id", Integer.valueOf(12345));
actual.put("id", Double.valueOf(12345));
JSONAssert.assertEquals(expected, actual, JSONCompareMode.LENIENT);
这里使用 JSONObject
替代字符串,且预期是 Integer
,实际是 Double
。由于逻辑值都是 12345,测试通过。✅
嵌套对象比较同样简单粗暴:
String result = "{id:1,name:\"Juergen\",
address:{city:\"Hollywood\", state:\"LA\", zip:91601}}";
JSONAssert.assertEquals("{id:1,name:\"Juergen\",
address:{city:\"Hollywood\", state:\"LA\", zip:91601}}", result, false);
3.5. 自定义断言消息
所有 assertEquals()
和 assertNotEquals()
方法都支持自定义错误消息:
String actual = "{id:123,name:\"John\"}";
String failureMessage = "Only one field is expected: name";
try {
JSONAssert.assertEquals(failureMessage,
"{name:\"John\"}", actual, JSONCompareMode.STRICT);
} catch (AssertionError ae) {
assertThat(ae.getMessage()).containsIgnoringCase(failureMessage);
}
测试失败时,错误信息更清晰:
Only one field is expected: name
Unexpected: id
第一行是自定义消息,第二行是库提供的补充信息。⚠️
4. 处理 JSON 数组
数组比较规则与对象略有不同。
4.1. 数组元素顺序
STRICT 模式下数组元素顺序必须完全一致,但 LENIENT 模式不关心顺序:
String result = "[Alex, Barbera, Charlie, Xavier]";
JSONAssert.assertEquals(
"[Charlie, Alex, Xavier, Barbera]", result, JSONCompareMode.LENIENT);
JSONAssert.assertEquals(
"[Alex, Barbera, Charlie, Xavier]", result, JSONCompareMode.STRICT);
JSONAssert.assertNotEquals(
"[Charlie, Alex, Xavier, Barbera]", result, JSONCompareMode.STRICT);
这在验证 API 返回的排序数组时特别实用。✅
4.2. 数组扩展元素
数组比较不允许扩展元素,即使 LENIENT 模式也不行:
String result = "[1,2,3,4,5]";
JSONAssert.assertEquals(
"[1,2,3,4,5]", result, JSONCompareMode.LENIENT);
JSONAssert.assertNotEquals(
"[1,2,3]", result, JSONCompareMode.LENIENT);
JSONAssert.assertNotEquals(
"[1,2,3,4,5,6]", result, JSONCompareMode.LENIENT);
无论哪种模式,预期数组必须与实际数组完全匹配。增删任何元素都会导致失败。❌
4.3. 数组特定操作
还有两种进阶技巧验证数组内容:
验证数组大小:通过具体语法指定预期大小:
String names = "{names:[Alex, Barbera, Charlie, Xavier]}";
JSONAssert.assertEquals(
"{names:[4]}",
names,
new ArraySizeComparator(JSONCompareMode.LENIENT));
"{names:[4]}"
表示预期数组大小为 4。✅
验证数值范围:确保所有元素在指定范围内:
String ratings = "{ratings:[3.2,3.5,4.1,5,1]}";
JSONAssert.assertEquals(
"{ratings:[1,5]}",
ratings,
new ArraySizeComparator(JSONCompareMode.LENIENT));
上述示例验证所有评分值在 [1,5]
区间内(含边界值)。超出范围会导致失败。⚠️
5. 高级比较示例
假设 API 返回多个 id
(均为整数),需用正则 \d
统一验证:
JSONAssert.assertEquals("{entry:{id:x}}", "{entry:{id:1, id:2}}",
new CustomComparator(
JSONCompareMode.STRICT,
new Customization("entry.id",
new RegularExpressionValueMatcher<Object>("\\d"))));
JSONAssert.assertNotEquals("{entry:{id:x}}", "{entry:{id:1, id:as}}",
new CustomComparator(JSONCompareMode.STRICT,
new Customization("entry.id",
new RegularExpressionValueMatcher<Object>("\\d"))));
{id:x}
中的 x
是占位符,实际匹配时用 \d
验证。由于 id
位于 entry
内部,需通过 Customization
指定路径。若任何 id
不匹配正则,测试失败。❌
6. 总结
本文通过多个场景展示了 JSONAssert 的实用价值:从简单比较到复杂验证。这个库能显著简化 JSON 测试的编写,尤其在处理动态字段或复杂结构时避免踩坑。
完整示例代码可在 GitHub 获取。