如何测试两个不同嵌套对象的属性值一致?解决StackOverflow错误
解决嵌套循环引用对象的属性相等测试问题
针对你遇到的嵌套循环引用导致StackOverflow、无法用toString对比的问题,给你几个实用的解决方案:
1. 手动编写属性对比方法
最直接的方式就是写一个静态工具方法,逐个对比两个对象的核心属性,主动跳过会引发循环引用的字段。比如你有A和B相互引用,对比时只检查各自的基本属性,不递归对比交叉引用的对象:
import java.util.Objects; public class ObjectComparator { public static boolean areAEqual(ObjectA a1, ObjectA a2) { if (a1 == a2) return true; if (a1 == null || a2 == null) return false; // 对比A的基本属性 if (!Objects.equals(a1.getId(), a2.getId())) return false; if (!Objects.equals(a1.getTitle(), a2.getTitle())) return false; // 对比嵌套的B对象,但只检查B的基本属性,跳过B中的A引用 return areBEqual(a1.getB(), a2.getB()); } public static boolean areBEqual(ObjectB b1, ObjectB b2) { if (b1 == b2) return true; if (b1 == null || b2 == null) return false; if (!Objects.equals(b1.getCode(), b2.getCode())) return false; if (!Objects.equals(b1.getDesc(), b2.getDesc())) return false; // 这里不要对比b.getA(),避免触发循环引用 return true; } }
测试时直接调用:
assertTrue(ObjectComparator.areAEqual(actualA, expectedA));
2. 重写equals和hashCode方法
如果可以修改实体类,重写equals方法时避开循环引用的字段,这样就能直接用JUnit的assertEquals断言:
比如在ObjectA的equals中:
@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ObjectA objectA = (ObjectA) o; return Objects.equals(id, objectA.id) && Objects.equals(title, objectA.title) && // 只对比B对象的equals,而B的equals里不对比A引用 Objects.equals(b, objectA.b); }
在ObjectB的equals中:
@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ObjectB objectB = (ObjectB) o; return Objects.equals(code, objectB.code) && Objects.equals(desc, objectB.desc); // 这里不对比a字段,避免循环 }
之后测试直接用:
assertEquals(expectedA, actualA);
3. 使用Mockito的refEq匹配器
如果你的项目里已经引入Mockito,可以用refEq()方法忽略指定的循环引用字段,它会对比对象的所有属性,除了你指定要排除的:
import static org.mockito.Mockito.refEq; // 断言时忽略ObjectA中b字段里的a引用 assertTrue(refEq(expectedA, "b.a").matches(actualA));
如果是验证方法调用,也可以直接用:
verify(myService).processA(refEq(expectedA, "b.a"));
4. 反射工具类对比(通用方案)
写一个基于反射的通用对比工具,通过记录已访问的对象来避免循环递归:
import java.lang.reflect.Field; import java.util.HashSet; import java.util.Objects; import java.util.Set; public class ReflectComparator { public static boolean equals(Object obj1, Object obj2) { return equals(obj1, obj2, new HashSet<>()); } private static boolean equals(Object obj1, Object obj2, Set<Object> visited) { if (obj1 == obj2) return true; if (obj1 == null || obj2 == null) return false; if (obj1.getClass() != obj2.getClass()) return false; // 避免重复对比同一对象,防止循环引用 if (visited.contains(obj1) || visited.contains(obj2)) return true; visited.add(obj1); visited.add(obj2); Field[] fields = obj1.getClass().getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); try { Object val1 = field.get(obj1); Object val2 = field.get(obj2); if (!equals(val1, val2, new HashSet<>(visited))) { return false; } } catch (IllegalAccessException e) { return false; } } return true; } }
测试时调用:
assertTrue(ReflectComparator.equals(actualA, expectedA));
内容的提问来源于stack exchange,提问作者User
相关产品推荐
相关产品推荐

