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 仓库


原始标题:assertEquals() vs. assertSame() in JUnit | Baeldung