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?
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
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);