Apache Beam管道PAssert校验对象相等仍抛出AssertionError怎么办
Apache Beam测试PAssert断言报错:对象标识一致仍匹配失败
问题现象
Apache Beam管道单元测试中,使用PAssert.containsInAnyOrder()校验ParDo转换输出的PCollection<UserPageViews>结果时触发AssertionError,错误日志中预期集合内的对象和实际输出的未匹配对象打印的标识完全一致。
测试代码
public void testGenerateUserPageViews() throws Exception{ final PCollection<SessionModel> input = p.apply(Create.of(SESSION_MODEL)); final PCollection<UserPageViews> output = input.apply(ParDo.of(new GenerateUserPageViews())); PAssert.that(output).containsInAnyOrder(USER_PAGEVIEWS); p.run().waitUntilFinish(); }
报错堆栈
java.lang.AssertionError: ParDo(GenerateUserPageViews)/ParMultiDo(GenerateUserPageViews).output: Expected: iterable over [<com.userprofile.models.UserPageViews@e1688b19>] in any order but: Not matched: <com.userprofile.models.UserPageViews@e1688b19> at org.apache.beam.sdk.testing.PAssert$PAssertionSite.capture(PAssert.java:174) at org.apache.beam.sdk.testing.PAssert.that(PAssert.java:416) at org.apache.beam.sdk.testing.PAssert.that(PAssert.java:408) at
根因分析
- 日志中对象后缀形如
@e1688b19的内容是对象hashCode()返回值转十六进制的结果,并非JVM中对象的实际内存地址,两个对象打印出相同的后缀,仅代表二者hashCode计算结果一致,不能证明对象逻辑相等、或是同一个引用。 - Beam的
PAssert做集合元素匹配时,默认依赖元素类的equals()方法判断两个对象是否匹配。如果UserPageViews类没有重写equals()方法,会调用Object类的默认实现,仅判断两个对象的引用地址是否完全一致——哪怕两个实例的所有字段值完全相同、hashCode一致,只要是不同的实例对象,就会被判定为不匹配。 - 若类中只重写了
hashCode()但未正确重写equals(),或是equals()实现遗漏了关键字段、逻辑不符合相等判断规范,也会触发该报错。
修复方案
- 为
UserPageViews类按照业务相等逻辑正确重写equals()和hashCode()方法,确保所有参与业务相等判定的字段都被纳入两个方法的实现逻辑。Java项目可直接使用Lombok的@Data或@EqualsAndHashCode注解自动生成符合规范的方法,避免手写遗漏字段。 - 若不希望修改生产侧模型类,可改用
PAssert提供的自定义断言匹配器,手动逐字段对比输出结果和预期值,但优先推荐重写模型类的equals和hashCode——Beam运行过程中的分组、去重等多类场景都会依赖这两个方法的正确实现。
避坑提示:不要被日志中看起来完全一致的
@类名@xxxx标识误导,该标识仅和hashCode返回值相关,和对象是否匹配没有绝对关联。
内容的提问来源于stack exchange,提问作者Mohamed Haydar
相关产品推荐
相关产品推荐

