Describe the bug
I needed to combine assertion with a custom comparator and recursive comparison of an unordered collection. At first, it worked like a charm, but in a very specific case, it started to fail. I spent hours debugging it and I think I know what causes that problem. In RecursiveComparisonDifferenceCalculator#initDualValuesToCompare() detected dual values are filtered to remove already visited cases. However, the equals() method of DualValue only checks actual and expected values, ignoring fieldLocation (there is a comment there questioning this btw ;) ). As a result, similar dual values created in different locations (e.g. the same object in different collections) are marked as similar and removed. Then, the collection of differences is empty and then compareUnorderedIterables() wrongly assumes it found a match and removes investigated value from its iterator...
- assertj core version: 3.24.2
- java version: 17.0.3
- test framework version: JUnit 5.8.2
Test case reproducing the bug
It sounds a bit complicated so I highly recommend seeing the example code below and running it with a debugger. Ofc it's not my real case, just a minimal example to reproduce the problem.
It will throw AssertionError with the message: The following expected elements were not matched in the actual List12: [pl.edu.agh.mobint.db.application.statistics.phonenumber.Example$DataStore@7331196b]. If you remove the custom comparator, the test passes.
public class Example {
class DataStore {
List<Data> store1 = new ArrayList<>();
List<Data> store2 = new ArrayList<>();
}
class Data {
private String text;
Data(String text) {
this.text = text;
}
public String getText() {
return text;
}
@Override
public String toString() {
return "Data: " + text;
}
}
@Test
void assertTest() {
var dataStore1 = createDataStore(true);
var dataStore2 = createDataStore(false);
assertThat(List.of(dataStore1)).usingRecursiveComparison(RecursiveComparisonConfiguration.builder()
.withComparatorForType(Comparator.comparing(Data::getText), Data.class)
.build())
.ignoringCollectionOrder()
.isEqualTo(List.of(dataStore2));
}
private DataStore createDataStore(boolean reverse) {
var data1 = new Data("123");
var data2 = new Data("456");
var data3 = new Data("987");
var dataStore = new DataStore();
dataStore.store1.addAll(reverse ? List.of(data3, data2, data1) : List.of(data1, data2, data3));
dataStore.store2.addAll(reverse ? List.of(data3, data2) : List.of(data2, data3));
return dataStore;
}
}
Describe the bug
I needed to combine assertion with a custom comparator and recursive comparison of an unordered collection. At first, it worked like a charm, but in a very specific case, it started to fail. I spent hours debugging it and I think I know what causes that problem. In
RecursiveComparisonDifferenceCalculator#initDualValuesToCompare()detected dual values are filtered to remove already visited cases. However, theequals()method ofDualValueonly checksactualandexpectedvalues, ignoringfieldLocation(there is a comment there questioning this btw ;) ). As a result, similar dual values created in different locations (e.g. the same object in different collections) are marked as similar and removed. Then, the collection of differences is empty and thencompareUnorderedIterables()wrongly assumes it found a match and removes investigated value from its iterator...Test case reproducing the bug
It sounds a bit complicated so I highly recommend seeing the example code below and running it with a debugger. Ofc it's not my real case, just a minimal example to reproduce the problem.
It will throw
AssertionErrorwith the message:The following expected elements were not matched in the actual List12: [pl.edu.agh.mobint.db.application.statistics.phonenumber.Example$DataStore@7331196b]. If you remove the custom comparator, the test passes.