我有一个班级MySchool
:
public class MySchool{
private long timestamp;
private SchoolEvent event;
private Object value;
//getter & setters
...
@Override
public String toString() {
return "MySchool [timestamp=" + timestamp + ", event="
+ event + ", value=" + value + "]";
}
}
SchoolEvent
是枚举类型:
public static enum SchoolEvent {
CEREMONY, HOLIDAY
}
我尝试使用Assert.assertEquals()来比较两个学校列表:
List<MySchool> schoolList1 = generateSchools();
List<MySchool> schoolList2 = readSchoolsFromFile();
Assert.assertEquals(schoolList1, schoolList2);
失败并出现以下错误:
expected: java.util.ArrayList<[MySchool [timestamp=0, event=CEREMONY, value=null], MySchool [timestamp=0, event=HOLIDAY, value=null]]>
but was: java.util.ArrayList<[MySchool [timestamp=0, event=CEREMONY, value=null], MySchool [timestamp=0, event=HOLIDAY, value=null]]>
我不明白为什么错误听起来不像是错误,我只是检查错误信息,两个列表中每个元素对象的每个字段都是euqal。
我还检查了关于List#equal的Java文档,它还说:
如果两个列表包含相同的元素,则它们被定义为相等 以相同的顺序。
为什么assertEquals()失败呢?
答案 0 :(得分:2)
我不明白为什么错误听起来不像是错误,我只是检查错误信息,两个列表中每个元素对象的每个字段都是euqal。
是的,但这并不意味着对象被认为是平等的。
您需要覆盖equals()
(和hashCode()
)才能使不同的对象被视为相等。默认情况下,equals
方法只检查对象身份 ...换句话说,x.equals(y)
相当于检查x
和y
默认情况下,请参阅完全相同的对象。如果您想要不同的行为 - 以便检查某些字段是否相等 - 那么您需要在equals()
中指定该行为,并以与此一致的方式实现hashCode()
。
请注意,此问题完全不依赖于集合。如果比较单个对象,你会遇到同样的问题:
MySchool school1 = schoolList1.get(0);
MySchool school2 = schoolList2.get(0);
Assert.areEqual(school1, school2); // This will fail...
答案 1 :(得分:1)
您的班级必须实施equals()。我将在下面添加一个示例实现:
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MySchool mySchool = (MySchool) o;
if (timestamp != mySchool.timestamp) return false;
if (event != mySchool.event) return false;
return !(value != null ? !value.equals(mySchool.value) : mySchool.value != null);
}
然后我会使用hamcrest在集合上断言:
public void testTwoEventsAreEquals() throws Exception {
List<MySchool> schoolList1 = Arrays.asList(new MySchool(SchoolEvent.CEREMONY), new MySchool(SchoolEvent.HOLIDAY));
List<MySchool> schoolList2 = Arrays.asList(new MySchool(SchoolEvent.CEREMONY), new MySchool(SchoolEvent.HOLIDAY));
assertThat(schoolList1, containsInAnyOrder(schoolList2.toArray()));
}
如果你正在使用maven,你必须添加Hamcrest作为依赖项,以便上面的代码编译。
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<version>1.3</version>
</dependency>
答案 2 :(得分:1)
不要按照别人的建议放置equals
方法(抱歉,伙计们)。这将是一个生产中的测试代码,它本身不仅是错误的,而且如果您向该类添加另一个字段但忘记更新equals
方法会怎么样?您的测试不会测试您认为他们正在测试的内容。如果equals
方法错了怎么办?如果它总是返回真实怎么办?你的测试总是绿色的,你会错误地确信一切都很好。因为当然谁会为equals
方法编写测试。
我强烈建议您使用shazamcrest等工具。这允许您只需编写assertThat(actual, sameBeanAs(expected))
,而不必担心任何事情。它不需要对您的生产代码进行任何更改。它将覆盖所有字段,无论其访问修饰符如何。还提供非常好的诊断。它会抛出ComparisonFailure
(而不是AssertionError
),IDE将为您提供很好的并排比较。
代码:
List<MySchool> schoolList1 = generateSchools();
List<MySchool> schoolList2 = readSchoolsFromFile();
assertThat(schoolList2, sameBeanAs(schoolList2));
需要依赖性:
<dependency>
<groupId>com.shazam</groupId>
<artifactId>shazamcrest</artifactId>
<version>0.11</version>
</dependency>
您获得的诊断:
注意:
请务必使用com.shazam.shazamcrest.MatcherAssert.assertThat
,而不是hamcrest
或junit
。它的工作方式与其他assertThat
方法完全相同,但如果与sameBeanAs
一起使用,则会抛出ComparisonFailure
,而不是AssertionError
。
答案 3 :(得分:0)
要使两个对象相等,它们的equals()
方法必须返回true。
如果两个对象都是SAME对象,则equals()的默认实现仅返回true,但如果它们是具有相同“内容”的不同对象则不返回。
所以,x.equals(y);
可以true
,即使x == y
(“x和y是同一个对象”)是false
,如果是equals()
对于参数y,x方法返回true
。