How to compare two collections that vary by properties using Fluent Assertion?

回眸只為那壹抹淺笑 提交于 2019-12-07 21:56:28

问题


I have public class RuleInfo which is created from internal class Rule.

private static RuleInfo CreateRuleInfo(Rule r)
{
    return new RuleInfo
    {
        RuleCode = r.RuleId,
        DisplayName = r.RuleCode,
        Description = r.Description,
        LegacyRuleCode = null
    };
}

They vary in their properties names so ShouldBeEquivalentTo() or ShouldAllBeEquivalentTo() don't work.

Right now I'm comparing them manually/explicitly:

foreach (var x in Enumerable.Zip(infs, rules, (i, r) => new { Info = i, Rule = r }))
{
    x.Info.ShouldBeEquivalentTo(
        new
        {
            RuleCode = x.Rule.RuleId,
            DisplayName = x.Rule.RuleCode,
            Description = x.Rule.Description,
            LegacyRuleCode = (string)null
        });
}

Is there a better, more compact, less explicit, more readable way?


回答1:


Unfortunately there currently isn't a way to specify a mapping between properties when comparing different types. There is an open issue about it.

Here's an example on another way to compare two collections. Be aware that I'm assuming that == performs value equality. So if all your properties are int and string you are home safe.

ruleInfos.Should().Equal(rules, (ruleInfo, rule) =>
    ruleInfo.RuleCode == rule.RuleId
     && ruleInfo.DisplayName == rule.RuleCode
    && ruleInfo.Description == rule.Description
);

For e.g. a reference type with no overload of == you would need to handle null values gracefully with e.g.

(PropertyA == PropertyB) || (PropertyA?.Equals(PropertyB) == true



回答2:


One option is adding custom equivalency step to global options configuration:

class DifferentObjectsEquivalencyStep<T1, T2> : IEquivalencyStep {
    private readonly Func<T1, T2> _converter;

    public DifferentObjectsEquivalencyStep(Func<T1, T2> converter) {
        _converter = converter;
    }

    public bool CanHandle(IEquivalencyValidationContext context, IEquivalencyAssertionOptions config) {
        return context.Subject is T1 && context.Expectation is T2 || context.Subject is T2 && context.Expectation is T1;
    }

    public bool Handle(IEquivalencyValidationContext context, IEquivalencyValidator parent, IEquivalencyAssertionOptions config) {            
        var first = context.Subject is T1 ? (T1) context.Subject : (T1) context.Expectation;
        var second = context.Subject is T2 ? (T2) context.Subject : (T2) context.Expectation;
        second.ShouldBeEquivalentTo(_converter(first));
        return true;
    }
}

Then somewhere before you do all comparisions:

AssertionOptions.AssertEquivalencyUsing(c => c.Using(
            new DifferentObjectsEquivalencyStep<Rule, RuleInfo>(CreateRuleInfo)));

After that, regular ShouldBeEquivalentTo (and ShouldAllBeEquivalentTo) will work:

rule.ShouldBeEquivalentTo(info);


来源:https://stackoverflow.com/questions/47946473/how-to-compare-two-collections-that-vary-by-properties-using-fluent-assertion

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!