由于使用 toString() 的循环引用,AssertJ 错误报告失败

AssertJ error reporting fails due to a circular reference with toString()

要测试的遗留代码具有循环引用,因此类似于多对一双向关系实体。

不幸的是,相关 类 中也有 Lombok 注释 @ToString@Data,每当调用 toString() 时就会导致堆栈溢出。

当然,我意识到这是一个有点糟糕的代码,如果确实需要 toString() 方法,应该有一些协议,并且需要的字段应该用 @ToString.Exclude 注释,以防止循环引用 toString()。然而,目前我不能触及这段代码,但我仍然想使用 AssertJ,例如:

assertThat(list1).containsExactly(item1, item2);

而不是做一些更复杂的测试代码。

现在的问题是,测试执行没有AssertJ报告断言失败,因为当AssertJ调用[=13时这个循环引用=] 在项目上报告失败的项目是什么。

是否可能,如果可以,我如何更改 AssertJ 以在不调用 toString() 的情况下报告失败,但是 - 例如 - getId() 失败的项目?

一种解决方案是对列表使用自定义表示: 假设 class Obj 循环引用它是 toString()。将自定义表示 class 定义为:

class CustomListRepresentation implements Representation {
    public static final CustomListRepresentation instance = new CustomListRepresentation();
    
    private CustomListRepresentation() {
    }
    @Override
    public String toStringOf(Object) {
        List<?> list = (List<?>) object;
        return list.stream()
                   .filter(Obj.class::isInstance)
                   .map(Obj.class::cast).map(Obj::getId)
                   .map(String::valueOf)
                   .collect(Collectors.joining(","));
    }

    @Override
    public String unambiguousToStringOf(Object object) {
        return this.toStringOf(object);
    }

}

如果除 Obj 之外的多个 classes 也有此问题,那么您可以保留这些 classes 的映射及其映射函数(用于代替 toString()) 可以定义并适当地替换 .filter(Obj.class::isInstance).map(Obj.class::cast).map(Obj::getId)

现在assert语句需要修改为:

assertThat(list).withRepresentation(CustomListRepresentation.instance)
                .containsExactly(item1, item2);

或者另一种方法是使用 overridingErrorMessage 方法覆盖错误消息:

private static String getError(List<Obj> list, Obj... items) {
    // return a custom formatted error message.....
}

断言为:

assertThat(list).overridingErrorMessage(getError(list, item1, item2))
                .containsExactly(item1, item2);

根据 我的最终结果(直到这些烦人的循环被重构掉)很简单:

Representation pres = new StandardRepresentation() {
    public String toStringOf(Object object) {
        return unambiguousToStringOf(object);
    }
    public String unambiguousToStringOf(Object object) {
        return (object == null) ? "null" : object.getClass().getSimpleName() + "@" + object.hashCode();
    }
};

在我的测试中就像这样(用一个愚蠢的例子class):

@ToString
public static class A {
    public A a;
}

@Test
void testNoError() {
    var a = new A();
    a.a = a;
    // Not null so AssretJ prints out failing object when test fails
    assertThat(a).withRepresentation(pres).isNull();
}