FluentAssertions 6 ObjectGraph 将枚举与字符串进行比较

FluentAssertions 6 ObjectGraph compare Enum to String

使用 FluentAssertions 6,您似乎可以不再验证对象图中的枚举是否等同于字符串。资料来源:https://fluentassertions.com/upgradingtov6

enum MyEnum {
   A,
   B
}

class Source {
   MyEnum Enum { get;set;}
}

class Expectation {
   string Enum { get;set;}
}

var source = new Source() { Enum = MyEnum.A };
var expectation = new Expectation() {Enum = "A"};

//With V6 this assertion will fail but in V5 it will pass
expectation.Should().BeEquivalentTo(source, options => options.ComparingEnumsByName());

如何使用 FluentAssertions 断言上述对象?我想要的行为是对枚举的 ToString 表示进行断言。

正如我边注,当我将 expectationsource 交换时,我得到了不同的行为。他们不应该是等价的吗?

您的期望将 Enum 属性 定义为字符串,您提供的选项用于指示 FA 如何将期望的成员与主题进行比较。所以在这种情况下,ComparingEnumsByName 不做任何事情,因为涉及的 属性 是 string。您可以做的是使用匿名类型作为期望。

您可以定义更宽松的 equivalency step 来处理 string/enum 比较。

class RelaxedEnumEquivalencyStep : IEquivalencyStep
{
    public EquivalencyResult Handle(Comparands comparands, IEquivalencyValidationContext context, IEquivalencyValidator nestedValidator)
    {
        if (comparands.Subject is string subject && comparands.Expectation?.GetType().IsEnum == true)
        {
            AssertionScope.Current
                .ForCondition(subject == comparands.Expectation.ToString())
                .FailWith(() =>
                {
                    decimal? subjectsUnderlyingValue = ExtractDecimal(comparands.Subject);
                    decimal? expectationsUnderlyingValue = ExtractDecimal(comparands.Expectation);

                    string subjectsName = GetDisplayNameForEnumComparison(comparands.Subject, subjectsUnderlyingValue);
                    string expectationName = GetDisplayNameForEnumComparison(comparands.Expectation, expectationsUnderlyingValue);
                    return new FailReason(
                            $"Expected {{context:string}} to be equivalent to {expectationName}{{reason}}, but found {subjectsName}.");
                });

            return EquivalencyResult.AssertionCompleted;
        }

        if (comparands.Subject?.GetType().IsEnum == true && comparands.Expectation is string expectation)
        {
            AssertionScope.Current
                .ForCondition(comparands.Subject.ToString() == expectation)
                .FailWith(() =>
                {
                    decimal? subjectsUnderlyingValue = ExtractDecimal(comparands.Subject);
                    decimal? expectationsUnderlyingValue = ExtractDecimal(comparands.Expectation);

                    string subjectsName = GetDisplayNameForEnumComparison(comparands.Subject, subjectsUnderlyingValue);
                    string expectationName = GetDisplayNameForEnumComparison(comparands.Expectation, expectationsUnderlyingValue);
                    return new FailReason(
                            $"Expected {{context:enum}} to be equivalent to {expectationName}{{reason}}, but found {subjectsName}.");
                });

            return EquivalencyResult.AssertionCompleted;
        }

        return EquivalencyResult.ContinueWithNext;
    }

    private static string GetDisplayNameForEnumComparison(object o, decimal? v)
    {
        if (o is null)
        {
            return "<null>";
        }

        if (v is null)
        {
            return '\"' + o.ToString() + '\"';
        }

        string typePart = o.GetType().Name;
        string namePart = o.ToString().Replace(", ", "|", StringComparison.Ordinal);
        string valuePart = v.Value.ToString(CultureInfo.InvariantCulture);
        return $"{typePart}.{namePart} {{{{value: {valuePart}}}}}";
    }

    private static decimal? ExtractDecimal(object o)
    {
        return o?.GetType().IsEnum == true ? Convert.ToDecimal(o, CultureInfo.InvariantCulture) : null;
    }
}

如果只是单测

var source = new Source() { Enum = MyEnum.A };
var expectation = new Expectation() { Enum = "A" };

expectation.Should().BeEquivalentTo(source, options => options.Using(new RelaxedEnumEquivalencyStep()));

source.Should().BeEquivalentTo(expectation, options => options.Using(new RelaxedEnumEquivalencyStep()));

或者,如果您希望在全局范围内使用它,您可以使用 AssertionOptions.AssertEquivalencyUsing.

进行设置
AssertionOptions.AssertEquivalencyUsing(e => e.Using(new RelaxedEnumEquivalencyStep()));

var source = new Source() { Enum = MyEnum.A };
var expectation = new Expectation() { Enum = "A" };

expectation.Should().BeEquivalentTo(source);
source.Should().BeEquivalentTo(expectation);

为了完整起见,这里是 MyEnumstring 不匹配时失败消息的示例。

Expected property root.Enum to be equivalent to "B", but found MyEnum.A {value: 0}.
Expected property source.Enum to be <null>, but found MyEnum.B {value: 1}.
Expected property root.Enum to be equivalent to MyEnum.A {value: 0}, but found "B".
Expected property expectation.Enum to be equivalent to MyEnum.B {value: 1}, but found <null>.