0

Using Should().BeEquivalentTo() I want to compare 2 objects which contain a series of key-value pairs but to exclude the actual value of the keys because that will vary. So basically I'm only interested in comparing the contents of the Values. Example:

MyObject { MyDictionary1 { Key, Value } ... MyDictionary2 { Key, Value } }

compared to

ExpecterdObject { ExpectedDictionary1 { Key, Value } ... ExpectedDictionary2 { Key, Value } }

The 2 objects being of the same class with the same structure but with each instance having unique ids as keys.

I already tried

.Excluding(e => e.KayValuePair.Keys)

which doesn't seem to work as I still get errors saying that

Expected member MyDictionary1 to be a collection with 3 item(s). Expected member MyDictionary1 to contain key X.

FSatmar
  • 11
  • 3

2 Answers2

3

If you have an object structure like MyClass, and you want to override how the Dictionary property is compared between two instances of MyClass. Namely that only the Values of the dictionaries are compared.

class MyClass
{
    public int Value { get; set; }

    public Dictionary<int, string> Dictionary1 { get; set; }

    public Dictionary<int, string> Dictionary2 { get; set; }
}

To do that you can use a combination of Using and WhenTypeIs from the documentation.

Here's a complete example for your case, where the dictionaries have the same Values, but the Keys differ.

public void MyTestMethod()
{
    var expected = new MyClass
    {
        Value = 42,
        Dictionary1 = new Dictionary<int, string>
        {
            [1] = "foo",
            [2] = "bar"
        },
        Dictionary2 = new Dictionary<int, string>
        {
            [3] = "bar",
            [4] = "baz"
        }
    };

    var actual = new MyClass
    {
        Value = 42,
        Dictionary1 = new Dictionary<int, string>
        {
            [5] = "foo",
            [6] = "bar",
        },
        Dictionary2 = new Dictionary<int, string>
        {
            [7] = "bar",
            [8] = "baz",
        }
    };

    actual.Should().BeEquivalentTo(expected, options => options.
        Using<Dictionary<int, string>>(ctx => ctx.Subject.Values.Should().BeEquivalentTo(ctx.Expectation.Values))
        .WhenTypeIs<Dictionary<int, string>>());
}
Jonas Nyrup
  • 2,376
  • 18
  • 25
  • Actually I wanted to do the opposite: exclude the Keys from the comparison and just compare the Values. Of course I can do MyDictionary.Values.Should().BeEquivalentTo(ExpectedDictionary.Values) but I'd like to do that when comparing 2 objects which contain lots of dictionaries, instead of comparing each dictionary on its own. – FSatmar Mar 23 '18 at 12:21
  • @FSatmar I've updated the example show, that it generalizes to objects that contain multiple dictionaries. – Jonas Nyrup Mar 23 '18 at 15:32
0

For anyone having this issue in recent times, I was encountering this same issue when my company used Dictionaries with unique strings as the key. I was able to resolve it by writing a custom IEquivalencyStep.

    public class DictionaryComparer : IEquivalencyStep
    {
        public bool CanHandle(IEquivalencyValidationContext context, IEquivalencyAssertionOptions config)
        {
            return context.Subject is IDictionary &&
                context.Expectation is IDictionary;
        }

        public bool Handle(IEquivalencyValidationContext context, IEquivalencyValidator parent, IEquivalencyAssertionOptions config)
        {
            var actual = (IDictionary)context.Subject;
            var expected = (IDictionary)context.Expectation;
            var actualValues = actual.Values;
            var expectedValues = expected.Values;

            actualValues.Should().BeEquivalentTo(expectedValues, options => options
            // Other exclusions, options, etc.
            );

            return true;
        }
    }

And then wherever you are making your FA equivalency check:

actual.Should().BeEquivalentTo(expected, options => options
.Using(new DictionaryComparer())
// Continue with other exclusions, options, etc...
);

After implementing it, all dictionaries within the parent object will be checked via the custom comparer you set up. You can change the CanHandle overload to look for whatever conditions you want and then only return true if they are met. This will cause the object that returned true from the CanHandle method to be evaluated through the Handle() method below it.

NOTE: This works in FluentAssertions 5, but I'm unsure if this changed in version 6.