Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Comparing Enums with custom Fluent Assertions Equivalency Step

I'm trying to write a custom Equivalency Step for Fluent Assertions to compare Enum values on the subject side back to a string on the exception side.

The problem I seem to be facing is that the subject type passed in to the IEquivalencyStep has been converted to a string before my EquivalencyStep is even invoked.

Is there some magic happening within fluent assertions that's trying to convert the enum straight in to a string?

An example of the code is below:

public class SubjectClass
{
    public EnumType Prop1 { get; set; }
}

public enum EnumType
{
    A,
    B,
    C
}

public class ExpectionClass
{
    public string Prop1 { get; set; }
}

[TestFixture]
public class ComparingEnumWithTests
{
    [Test]
    public void Test()
    {
        var expection = new ExpectionClass() {Prop1 = "bbb"};

        var subject = new SubjectClass() {Prop1 = EnumType.B};

        subject.ShouldBeEquivalentTo(expection, opt => opt.Using(new ComparingEnumWith<EnumType>(new Dictionary<string, EnumType>()
        {
            {"aaa", EnumType.A },
            {"bbb", EnumType.B },
            {"ccc", EnumType.C }
        })));

    }
}

public class ComparingEnumWith<TEnum> : IEquivalencyStep
    where TEnum : struct
{
    private readonly IReadOnlyDictionary<string, TEnum> _dictionary;
    private readonly Type _enumType;

    public ComparingEnumWith(IReadOnlyDictionary<string, TEnum> dictionary)
    {
        _enumType = typeof(TEnum);
        if (!_enumType.IsEnum)
        {
            throw new ArgumentException("TEnum must be an enum");
        }

        _dictionary = dictionary;
    }

    public bool CanHandle(IEquivalencyValidationContext context, IEquivalencyAssertionOptions config)
    {
        var subjectType = config.GetSubjectType(context);

        return subjectType != null && subjectType == _enumType && context.Expectation is string;
    }

    public bool Handle(IEquivalencyValidationContext context, IEquivalencyValidator parent, IEquivalencyAssertionOptions config)
    {
        var expected = _dictionary[(string)context.Expectation];

        return ((TEnum)context.Subject).Equals(expected);
    }
}

UPDATE:

From the problem above I've adapted the code to the below incase anyone else is having the same problem.

public class SubjectClass
{
    public EnumType Prop1 { get; set; }
}

public enum EnumType
{
    A,
    B,
    C
}

public class ExpectionClass
{
    public string Prop1 { get; set; }
}

[TestFixture]
public class ComparingEnumWithTests
{
    [Test]
    public void Test()
    {
        var expection = new ExpectionClass() {Prop1 = "bbb"};

        var subject = new SubjectClass() {Prop1 = EnumType.B};
        AssertionOptions.EquivalencySteps.Insert<ComparingEnumWith<EnumTypeDataMapProvider, EnumType>>();
        subject.ShouldBeEquivalentTo(expection);

    }
}

public interface IEnumDataMapProvider<TEnum>
{
    IReadOnlyDictionary<string, TEnum> Map { get; }
}

public class EnumTypeDataMapProvider : IEnumDataMapProvider<EnumType>
{
    public IReadOnlyDictionary<string, EnumType> Map => new Dictionary<string, EnumType>()
    {
        {"aaa", EnumType.A},
        {"bbb", EnumType.B},
        {"ccc", EnumType.C}
    };
}


public class ComparingEnumWith<TMapProvider, TEnum> : IEquivalencyStep
    where TMapProvider : IEnumDataMapProvider<TEnum>
    where TEnum : struct
{
    private readonly IReadOnlyDictionary<string, TEnum> _dictionary;
    private readonly Type _enumType;

    public ComparingEnumWith()
    {
        _enumType = typeof(TEnum);
        if (!_enumType.IsEnum)
        {
            throw new ArgumentException("TEnum must be an enum");
        }

        var provider = Activator.CreateInstance<TMapProvider>();

        _dictionary = provider.Map;
    }

    public bool CanHandle(IEquivalencyValidationContext context, IEquivalencyAssertionOptions config)
    {
        var subjectType = config.GetSubjectType(context);

        return subjectType != null && subjectType == _enumType && context.Expectation is string;
    }

    public bool Handle(IEquivalencyValidationContext context, IEquivalencyValidator parent, IEquivalencyAssertionOptions config)
    {
        var expected = _dictionary[(string)context.Expectation];

        return ((TEnum)context.Subject).Equals(expected);
    }
}
like image 321
Kevin Smith Avatar asked Sep 26 '22 06:09

Kevin Smith


1 Answers

That's a bug. You're registering something we call a user equivalency step. But these run after the built-in TryConversionEquivalencyStep and ReferenceEqualityEquivalencyStep. If you can please file this as a bug, we can look at it. As a workaround, consider inserting your custom step before all the built-in steps using AssertionOptions.EquivalencySteps.Insert<ComparingEnumWith<T>>();

like image 116
Dennis Doomen Avatar answered Sep 28 '22 05:09

Dennis Doomen