1. 概述
JUnit 是 Java 生态中主流的测试框架。其 API 提供了便捷的对象校验方式,但 assertEquals()
和 assertSame()
的区别常让开发者踩坑。本文将通过 JUnit 5 示例,深入剖析这两个方法的本质差异。
2. 对象同一性与相等性
对象比较涉及两个核心概念:同一性(Identity)和相等性(Equality):
- ✅ 同一性:检查两个引用是否指向堆内存中的同一对象(类似物理世界的"同一个人")
- ✅ 相等性:检查两个对象的内容是否等价(类似"两个身高相同的人")
经典类比:所有人看到的太阳是同一个对象(同一性),但不同照片中的太阳是不同表示(相等性)。
关键区别:
- 同一的对象必然相等
- 相等的对象未必同一(如两个内容相同的字符串实例)
3. Java 中的 equals()
与 ==
在 Java 中,这两个概念通过 ==
和 equals()
实现:
3.1 基础示例
@ParameterizedTest
@ValueSource(strings = {"Hello", "World"})
void givenSameString_WhenCompare_ThenBothTrue(String str) {
assertTrue(str.equals(str)); // 相等性成立
assertTrue(str == str); // 同一性成立
}
3.2 不同实例的陷阱
@ParameterizedTest
@ValueSource(strings = {"Hello", "World"})
void givenNewStrings_WhenCompare_ThenOnlyEqualsTrue(String str) {
// 两个内容相同但内存地址不同的对象
assertTrue(new String(str).equals(new String(str))); // ✅ 相等性成立
assertFalse(new String(str) == new String(str)); // ❌ 同一性不成立
}
3.3 自定义类的坑
未重写 equals()
时,即使内容相同,对象也不相等:
public class Person {
private final String firstName;
private final String lastName;
// 构造器/getter/setter...
}
@Test
void givenPeopleWithoutEquals_WhenCompare_ThenNotEqual() {
Person p1 = new Person("John", "Doe");
Person p2 = new Person("John", "Doe");
assertNotEquals(p1, p2); // ❌ 实际调用 Object.equals(),比较的是引用
}
⚠️ 根本原因:
Object
类的默认equals()
实现本质是==
:public boolean equals(Object obj) { return (this == obj); }
解决方案:必须重写
equals()
和hashCode()
(参考 Java 规范)
4. JUnit 中的断言方法
4.1 assertEquals()
vs assertSame()
方法 | 比较依据 | 适用场景 |
---|---|---|
assertEquals() |
equals() |
校验对象内容等价性 |
assertSame() |
== |
校验是否为同一对象实例 |
4.2 实战对比
@ParameterizedTest
@ValueSource(strings = {"Hello", "World"})
void givenSameObject_BothAssertionsPass(String str) {
assertEquals(str, str); // ✅ 内容相同
assertSame(str, str); // ✅ 同一对象
}
@ParameterizedTest
@ValueSource(strings = {"Hello", "World"})
void givenDifferentInstances_EqualsPass_SameFail(String str) {
String s1 = new String(str);
String s2 = new String(str);
assertEquals(s1, s2); // ✅ 内容相同
assertNotSame(s1, s2); // ✅ 不是同一对象
}
5. 总结
- ✅ **
assertEquals()
**:基于equals()
比较内容,适用于业务逻辑校验 - ✅ **
assertSame()
**:基于==
比较引用,适用于单例/缓存等场景 - ⚠️ 避坑指南:自定义类务必重写
equals()
和hashCode()
理解同一性与相等性的差异,能避免测试中的隐性 bug,写出更健壮的测试代码。完整示例代码见 GitHub 仓库。