0

How can I write a unit test such that the following two objects are considered equal? And in general how can I ensure collections nested arbitrarily deeply are compared against counterpart via their matching index location (Lists) and matching key (Dictionaries)? I attempted the following with the Fluent Assertions library but received the following error.

List<Dictionary<List<string>, string>> list1 = new(){
  new (){{new (){"a"}, "aaa"}, {new (){"z"}, "zzz"}},
  new (){{new (){"b"}, "bbb"}},
  new (){{new (){"c"}, "ccc"}}
};
List<Dictionary<List<string>, string>> list2 = new(){
  new (){{new (){"a"}, "aaa"}, {new (){"z"}, "zzz"}},
  new (){{new (){"b"}, "bbb"}},
  new (){{new (){"c"}, "ccc"}}
};

list1.Should().BeEquivalentTo(list2);
Unhandled exception. FluentAssertions.Execution.AssertionFailedException: Expected list1[0] to be a dictionary with 2 item(s), but it misses key(s) {{"a"}, {"z"}} and has additional key(s) {{"b"}}
Expected list1[1] to contain key {"b"}.
Expected list1[2] to contain key {"c"}.

With configuration:
- Use declared types and members
- Compare enums by value
- Compare tuples by their properties
- Compare anonymous types by their properties
- Compare records by their members
- Include non-browsable members
- Include all non-private properties
- Include all non-private fields
- Match member by name (or throw)
- Be strict about the order of items in byte arrays
- Without automatic conversion.
AffluentOwl
  • 3,337
  • 5
  • 22
  • 32

1 Answers1

0

My original question is asking a bit much out of the box from FluentAssertions as the keys stored in the Dictionary are references to lists, so it's very hard to reason about what should occur without quite specialized logic. Note that the following is allowed, and without more info it isn't clear how to design a matcher for all the duplicate List = {"a"} keys:

List<Dictionary<List<string>, string>> list1 = new(){
  new (){{new (){"a"}, "aaa"}, {new (){"a"}, "zzz"}, {new (){"a"}, "yyy"}},
  new (){{new (){"b"}, "bbb"}},
  new (){{new (){"c"}, "ccc"}}
};

The FluentAssertions error messages in my question are quite misleading, since they pretty prints that certain keys are missing misses key(s) {{"a"}, {"z"}} which exactly matches the pretty printing of the actual keys in the Dictionary, but it's not the pretty printed value that's being compared it's the reference locations which are compared. It would be much nicer if FluentAssertions pretty printed a representation of what was actually compared.

Note that adding the WithStrictOrdering significantly improves the error messages, versus the error message in my original question, and helped me debug the issue. Another thing that lead me astray was the lack nested collections as an example in the documentation, which I consider a common scenario.

For my needs the following is enough:

List<Dictionary<string, List<string>>> list1 = new(){
  new (){{"aaa", new (){"a"}}, {"zzz", new (){"z"}}},
  new (){{"bbb", new (){"b"}}},
  new (){{"ccc", new (){"c"}}},
};
List<Dictionary<string, List<string>>> list2 = new(){
  new (){{"zzz", new (){"z"}}, {"aaa", new (){"a"}}},
  new (){{"bbb", new (){"b"}}},
  new (){{"ccc", new (){"c"}}},
};
List<Dictionary<string, List<string>>> list3 = new(){
  new (){{"zzz", new (){"z"}}, {"aaa", new (){"a"}}},
  new (){{"ccc", new (){"c"}}},
  new (){{"bbb", new (){"b"}}},
};

list1.Should().BeEquivalentTo(list2);  // Matches
list1.Should().BeEquivalentTo(list2, options => options.WithStrictOrdering());  // Matches
list1.Should().BeEquivalentTo(list3);  // Matches
list1.Should().BeEquivalentTo(list3, options => options.WithStrictOrdering());
// Exception:
// Expected list1[1] to contain key "ccc".
// Expected list1[2] to contain key "bbb".
AffluentOwl
  • 3,337
  • 5
  • 22
  • 32