使用AssertJ的containsOnly()比较自定义对象数组失败问题咨询
containsOnly()对自定义对象数组不生效? 先看我的自定义对象定义:
@Accessors(chain = true) @Getter @Setter @AllArgsConstructor @ToString public class SocialLink { private String platform; private String link; }
测试代码如下:
SocialLink[] arr1 = new SocialLink[] { new SocialLink("Facebook", "Facebook Link"), new SocialLink("Google", "Google Link") }; SocialLink[] arr2 = new SocialLink[] { new SocialLink("Google", "Google Link"), new SocialLink("Facebook", "Facebook Link") }; assertThat(arr1).containsOnly(arr2);
运行后抛出断言错误:
java.lang.AssertionError: Expecting: <[SocialLink(platform=Facebook, link=Facebook Link), SocialLink(platform=Google, link=Google Link)]> to contain only: <[SocialLink(platform=Google, link=Google Link), SocialLink(platform=Facebook, link=Facebook Link)]> elements not found: <[SocialLink(platform=Google, link=Google Link), SocialLink(platform=Facebook, link=Facebook Link)]> and elements not expected: <[SocialLink(platform=Facebook, link=Facebook Link), SocialLink(platform=Google, link=Google Link)]>
明明containsOnly()对字符串数组能正常工作,为什么自定义对象数组就不行呢?
这个问题我之前也碰到过,核心原因其实是对象相等性的判断逻辑在搞鬼!
为什么字符串数组没问题?
String类已经默认重写了equals()和hashCode()方法,它比较的是字符串的内容而非对象的内存地址。所以哪怕是两个不同的String实例,只要内容一致,equals()就会返回true,AssertJ也就会认为它们是相同的元素。
为什么自定义对象不行?
你的SocialLink类没有重写equals()和hashCode(),这时候会沿用父类Object的默认实现——只比较对象的内存地址。你在arr1和arr2里创建的SocialLink都是new出来的全新实例,内存地址完全不同,所以AssertJ会把它们判定为完全不同的元素,自然断言失败。
两种快速解决办法
1. 用Lombok注解自动生成相等性判断逻辑
给SocialLink类加上@EqualsAndHashCode注解,Lombok会自动根据类中的字段(这里是platform和link)生成对应的equals()和hashCode()方法:
@Accessors(chain = true) @Getter @Setter @AllArgsConstructor @ToString @EqualsAndHashCode // 新增这个注解 public class SocialLink { private String platform; private String link; }
这样一来,只要两个SocialLink对象的platform和link内容一致,equals()就会返回true,containsOnly()就能正常识别匹配的元素了。
2. 手动指定AssertJ的元素比较器
如果不想修改实体类,也可以在断言时手动指定比较逻辑,比如用usingElementComparator():
assertThat(arr1) .usingElementComparator((o1, o2) -> { if (o1.getPlatform().equals(o2.getPlatform()) && o1.getLink().equals(o2.getLink())) { return 0; } return 1; }) .containsOnly(arr2);
或者用AssertJ提供的更简洁的字段比较器:
assertThat(arr1) .usingElementComparatorComparingOnlyFields("platform", "link") .containsOnly(arr2);
内容的提问来源于stack exchange,提问作者iamcrypticcoder

