JUnit中的assertEquals()与assertSame()

2025/03/15

1. 概述

JUnit是一个流行的测试框架,作为其API的一部分,它提供了一种检查和比较对象的便捷方法。然而,assertEquals()和assertSame()这两个方法之间的区别并不总是很明显

在本教程中,我们将检查assertEquals()和assertSame()方法。它们存在于JUnit 4和JUnit 5中,并且行为相同。但是,在本文中,我们将在示例中使用JUnit 5

2. 标识和相等性

当我们比较两个对象时,我们使用两个概念:标识和相等性。标识检查两个对象或元素是否相同,例如,两个人在物理意义上不能被视为相同。在计算机科学中,这更容易,因为我们使用引用的概念。

人们在世界任何地方看到的太阳都是相同的,电影或照片中的任何太阳图像都与我们透过窗户看到的太阳相同。因此,我们对同一个底层物体有不同的表述

同时,相等性不一定考虑对象标识,而是检查它们是否可以被视为相等。它是一个更灵活的概念,可以根据上下文以不同的方式表示。例如,人们不能被视为相同(在物理意义上),但可以在不同方面相等:身高、年龄、职业等。

两个相同的对象默认是相等的,但是两个相等的对象不一定相同。

3. equals()和==

在Java中,标识和相等性的概念可以用==和equals()方法表示。我们可以通过Strings看到它们的行为:

@ParameterizedTest
@ValueSource(strings = {"Hello", "World"})
void givenAString_WhenCompareInJava_ThenItEqualsAndSame(String string) {
    assertTrue(string.equals(string));
    assertTrue(string == string);
}

这表明,如果我们有相同的对象,它将与自身相同且相等。但是,如果我们有具有相同内容的不同实例,我们将得到不同的结果:

@ParameterizedTest
@ValueSource(strings = {"Hello", "World"})
void givenAStrings_WhenCompareNewStringsInJava_ThenItEqualsButNotSame(String string) {
    assertTrue(new String(string).equals(new String(string)));
    assertFalse(new String(string) == new String(string));
}

在这里,对象相等但不完全相同。有时,当我们不提供equals()和hashCode()方法的实现时,我们可能会遇到类问题:

public class Person {
    private final String firstName;
    private final String lastName;

    // constructors, getters, and setters
}

即使值相同,两个实例也不会完全相同,根据上面的解释,这是合理的。但是,它们也不会相等

@Test
void givePeople_WhenCompareWithoutOverridingEquals_TheyNotEqual() {
    Person firstPerson = new Person("John", "Doe");
    Person secondPerson = new Person("John", "Doe");
    assertNotEquals(firstPerson, secondPerson);
}

这是因为我们将使用Object类中的equals()方法的实现:

public boolean equals(Object obj) {
    return (this == obj);
}

我们可以看到,默认行为会回归到标识比较,并检查引用而不是对象的内容。这就是为什么提供equals()和hashCode()方法的有效实现很重要。

4. JUnit

熟悉了标识和相等性的概念之后,理解assertEquals()和assertSame()方法的行为就容易多了。第一个是使用equals()方法来比较元素

@ParameterizedTest
@ValueSource(strings = {"Hello", "World"})
void givenAString_WhenCompare_ThenItEqualsAndSame(String string) {
    assertEquals(string, string);
    assertSame(string, string);
}

同时,第二个使用标识并检查两个对象是否指向堆中的同一位置:

@ParameterizedTest
@ValueSource(strings = {"Hello", "World"})
void givenAStrings_WhenCompareNewStrings_ThenItEqualsButNotSame(String string) {
    assertEquals(new String(string), new String(string));
    assertNotSame(new String(string), new String(string));
}

5. 总结

标识和相等性是相关概念,但在比较过程中它们考虑的内容有很大不同,理解这些概念可以帮助我们避免细微的错误并编写更强大的代码。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章