Assert.assertEquals 在两个列表中
Assert.assertEquals on two lists
我有一个 class 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]]>
我不明白为什么错误听起来不像错误,我的意思是检查错误信息,两个列表中每个元素对象的每个字段都是相等的。
我还检查了 Java 关于 List#equal 的文档,它还说:
two lists are defined to be equal if they contain the same elements
in the same order.
为什么 assertEquals() 失败了?
I don't understand why the error doesn't sound like an error, I mean just check the error message, every field of every element object in two lists are euqal.
是的,但这并不意味着对象被认为是相等的。
您需要重写 equals()
(和 hashCode()
)才能将不同的对象视为相等。默认情况下,equals
方法只检查对象 identity... 换句话说,x.equals(y)
等同于检查 x
和 [=默认情况下,16=] 指的是 完全相同的对象 。如果您想要不同的行为 - 以便它检查某些字段是否相等 - 那么您需要在 equals()
中指定该行为,并以与此一致的方式实施 hashCode()
。
注意这个问题根本不依赖于集合。如果你比较单个对象,你会遇到同样的问题:
MySchool school1 = schoolList1.get(0);
MySchool school2 = schoolList2.get(0);
Assert.areEqual(school1, school2); // This will fail...
要使两个对象相等,它们的 equals()
方法必须 return 为真。
如果两个对象是同一对象,equals() 的默认实现仅 return 为真,但如果它们是具有相同 "content".
的不同对象,则不是
所以,x.equals(y);
可以是true
,即使x == y
("are x and y the same object")是false
,如果x的equals()
方法 returns true
参数 y.
您的 class 必须实施 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>
不要按照其他人的建议放置 equals
方法(对不起,伙计们)。这将是生产中的测试代码,它不仅本身是错误的,而且如果您向 class 添加另一个字段但忘记更新 equals
方法怎么办?你的测试不会测试你认为他们正在测试的东西。如果 equals
方法错误怎么办?如果它总是 returns 真怎么办?您的测试将始终为绿色,并且您会错误地认为一切都很好。因为当然谁会为 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
。
如果您使用 lombok 构建 classes 添加
@EqualsAndHashCode
注释在 class 定义之前也解决了这个问题
示例片段
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.EqualsAndHashCode;
import lombok.NonNull;
import java.math.BigDecimal;
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
public class Myclass {
@NonNull
private String var1;
@NonNull
private String var2;
@NonNull
private String var3;
@NonNull
private String var4;
@NonNull
private BigDecimal var5;
}
我有一个 class 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]]>
我不明白为什么错误听起来不像错误,我的意思是检查错误信息,两个列表中每个元素对象的每个字段都是相等的。
我还检查了 Java 关于 List#equal 的文档,它还说:
two lists are defined to be equal if they contain the same elements in the same order.
为什么 assertEquals() 失败了?
I don't understand why the error doesn't sound like an error, I mean just check the error message, every field of every element object in two lists are euqal.
是的,但这并不意味着对象被认为是相等的。
您需要重写 equals()
(和 hashCode()
)才能将不同的对象视为相等。默认情况下,equals
方法只检查对象 identity... 换句话说,x.equals(y)
等同于检查 x
和 [=默认情况下,16=] 指的是 完全相同的对象 。如果您想要不同的行为 - 以便它检查某些字段是否相等 - 那么您需要在 equals()
中指定该行为,并以与此一致的方式实施 hashCode()
。
注意这个问题根本不依赖于集合。如果你比较单个对象,你会遇到同样的问题:
MySchool school1 = schoolList1.get(0);
MySchool school2 = schoolList2.get(0);
Assert.areEqual(school1, school2); // This will fail...
要使两个对象相等,它们的 equals()
方法必须 return 为真。
如果两个对象是同一对象,equals() 的默认实现仅 return 为真,但如果它们是具有相同 "content".
的不同对象,则不是所以,x.equals(y);
可以是true
,即使x == y
("are x and y the same object")是false
,如果x的equals()
方法 returns true
参数 y.
您的 class 必须实施 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>
不要按照其他人的建议放置 equals
方法(对不起,伙计们)。这将是生产中的测试代码,它不仅本身是错误的,而且如果您向 class 添加另一个字段但忘记更新 equals
方法怎么办?你的测试不会测试你认为他们正在测试的东西。如果 equals
方法错误怎么办?如果它总是 returns 真怎么办?您的测试将始终为绿色,并且您会错误地认为一切都很好。因为当然谁会为 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
。
如果您使用 lombok 构建 classes 添加
@EqualsAndHashCode
注释在 class 定义之前也解决了这个问题
示例片段
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.EqualsAndHashCode;
import lombok.NonNull;
import java.math.BigDecimal;
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
public class Myclass {
@NonNull
private String var1;
@NonNull
private String var2;
@NonNull
private String var3;
@NonNull
private String var4;
@NonNull
private BigDecimal var5;
}