Legacy code to be tested has circular references so something like many-to-one bidirectional relationship entities.
Unfortunately there are also Lombok annotations @ToString
& @Data
in related classes which then result into a stack overflow whenever toString()
is called.
Of course I realize that this is a bit bad code and if toString()
methods really are needed there should be some agreement and needed fields should be annotated with @ToString.Exclude
to prevent circular reference with toString()
. However it is not currently up to me to touch this code but I still would like to use AssertJ, for example like:
assertThat(list1).containsExactly(item1, item2);
instead of doing some more complicated testing code.
The problem now is that instead of AssertJ reporting assertion failure the test execution fails because this circular reference when AssertJ calls toString()
on items to report what were the failing items.
Is it possible and if then how can I change AssertJ to report failures without calling the toString()
but - for example - getId()
on failing items?
One solution would be to use a Custom representation for the lists:
Assuming the class Obj
which has circular reference to it's toString()
. Define a custom representation class as:
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);
}
}
If multiple classes other than Obj
also have this issue then you could keep a map of these classes along with their mapping functions (to be used instead of toString()
) can be defined and replace the .filter(Obj.class::isInstance).map(Obj.class::cast).map(Obj::getId)
appropriately.
Now the assert statement need to be modified as:
assertThat(list).withRepresentation(CustomListRepresentation.instance)
.containsExactly(item1, item2);
Or a different way would be to override the error message using the overridingErrorMessage
method:
private static String getError(List<Obj> list, Obj... items) {
// return a custom formatted error message.....
}
Assert as:
assertThat(list).overridingErrorMessage(getError(list, item1, item2))
.containsExactly(item1, item2);