Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve enum comparison assert messages #923

Merged
merged 13 commits into from Sep 27, 2018
Merged
72 changes: 64 additions & 8 deletions Src/FluentAssertions/Equivalency/EnumEqualityStep.cs
@@ -1,7 +1,9 @@
#region

using System;
using System.Globalization;
using System.Reflection;
using FluentAssertions.Execution;

#endregion

Expand Down Expand Up @@ -36,17 +38,11 @@ public bool CanHandle(IEquivalencyValidationContext context, IEquivalencyAsserti
switch (config.EnumEquivalencyHandling)
{
case EnumEquivalencyHandling.ByValue:
decimal? subjectsUnderlyingValue = (context.Subject != null) ? Convert.ToDecimal(context.Subject) : (decimal?)null;
decimal? expectationsUnderlyingValue = (context.Expectation != null) ? Convert.ToDecimal(context.Expectation) : (decimal?)null;

subjectsUnderlyingValue.Should().Be(expectationsUnderlyingValue, context.Because, context.BecauseArgs);
HandleByValue(context);
break;

case EnumEquivalencyHandling.ByName:
string subject = context.Subject.ToString();
string expected = context.Expectation.ToString();

subject.Should().Be(expected, context.Because, context.BecauseArgs);
HandleByName(context);
break;

default:
Expand All @@ -56,5 +52,65 @@ public bool CanHandle(IEquivalencyValidationContext context, IEquivalencyAsserti

return true;
}

private static void HandleByValue(IEquivalencyValidationContext context)
{
decimal? subjectsUnderlyingValue = ExtractDecimal(context.Subject);
decimal? expectationsUnderlyingValue = ExtractDecimal(context.Expectation);

Execute.Assertion
.ForCondition(subjectsUnderlyingValue == expectationsUnderlyingValue)
.FailWith(() =>
{
string subjectsName = EnumDescription(context.Subject, subjectsUnderlyingValue);
string expectationName = EnumDescription(context.Expectation, expectationsUnderlyingValue);

return new FailReason($"Expected enum to equal {expectationName} by value{{reason}}, but found {subjectsName}.");
krajek marked this conversation as resolved.
Show resolved Hide resolved
});
}

private static void HandleByName(IEquivalencyValidationContext context)
{
string subject = context.Subject.ToString();
krajek marked this conversation as resolved.
Show resolved Hide resolved
string expected = context.Expectation.ToString();

Execute.Assertion
.ForCondition(subject == expected)
.FailWith(() =>
{
decimal? subjectsUnderlyingValue = ExtractDecimal(context.Subject);
decimal? expectationsUnderlyingValue = ExtractDecimal(context.Expectation);

string subjectsName = EnumDescription(context.Subject, subjectsUnderlyingValue);
krajek marked this conversation as resolved.
Show resolved Hide resolved
string expectationName = EnumDescription(context.Expectation, expectationsUnderlyingValue);
return new FailReason(
$"Expected enum to equal {expectationName} by name{{reason}}, but found {subjectsName}.");
});
}

private static string EnumDescription(object o, decimal? v)
krajek marked this conversation as resolved.
Show resolved Hide resolved
{
string PrintDecimal(decimal x) => x.ToString(CultureInfo.InvariantCulture);
krajek marked this conversation as resolved.
Show resolved Hide resolved

if (o == null || v == null)
{
return "null";
}

if (o.GetType().GetTypeInfo().IsEnum)
{
string typePart = o.GetType().Name;
string namePart = Enum.GetName(o.GetType(), o);
string valuePart = PrintDecimal(v.Value) ?? "?";
krajek marked this conversation as resolved.
Show resolved Hide resolved
return $"{typePart}.{namePart}({valuePart})";
}

return PrintDecimal(v.Value);
}

private static decimal? ExtractDecimal(object o)
{
return o != null ? Convert.ToDecimal(o) : (decimal?)null;
}
}
}
44 changes: 35 additions & 9 deletions Tests/Shared.Specs/BasicEquivalencySpecs.cs
Expand Up @@ -3532,11 +3532,7 @@ public void When_asserting_different_enum_members_are_equivalent_it_should_fail(
// Assert
//-----------------------------------------------------------------------------------------------------------
act.Should().Throw<XunitException>()
#if NETCOREAPP1_1
.WithMessage("Expected value to be*3*, but found*0*");
#else
.WithMessage("Expected EnumOne.One to be*3*, but found*0*");
#endif
.WithMessage("Expected *EnumOne.Two(3)*but*EnumOne.One(0)*");
}

[Fact]
Expand Down Expand Up @@ -3566,7 +3562,7 @@ public void When_asserting_members_from_different_enum_types_are_equivalent_by_v
// Arrange
//-----------------------------------------------------------------------------------------------------------
var subject = new ClassWithEnumOne { Enum = EnumOne.One };
var expectation = new ClassWithEnumThree { Enum = EnumeThree.ValueZero };
var expectation = new ClassWithEnumThree { Enum = EnumThree.ValueZero };

//-----------------------------------------------------------------------------------------------------------
// Act
Expand All @@ -3586,7 +3582,7 @@ public void When_asserting_members_from_different_enum_types_are_equivalent_by_s
// Arrange
//-----------------------------------------------------------------------------------------------------------
var subject = new ClassWithEnumOne { Enum = EnumOne.Two };
var expectation = new ClassWithEnumThree { Enum = EnumeThree.Two };
var expectation = new ClassWithEnumThree() { Enum = EnumThree.Two};

//-----------------------------------------------------------------------------------------------------------
// Act
Expand All @@ -3599,6 +3595,26 @@ public void When_asserting_members_from_different_enum_types_are_equivalent_by_s
act.Should().NotThrow();
}

[Fact]
public void When_asserting_members_from_different_enum_types_are_equivalent_by_value_but_comparing_by_name_it_should_throw()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 What are asserting members?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you accept the change to "[...]asserting_that_members_from[...]"? BTW there are many places like this is this particular file and tens in other files :-).

{
//-----------------------------------------------------------------------------------------------------------
// Arrange
//-----------------------------------------------------------------------------------------------------------
var subject = new ClassWithEnumOne { Enum = EnumOne.Two };
var expectation = new ClassWithEnumFour { Enum = EnumFour.Three };

//-----------------------------------------------------------------------------------------------------------
// Act
//-----------------------------------------------------------------------------------------------------------
Action act = () => subject.Should().BeEquivalentTo(expectation, config => config.ComparingEnumsByName());

//-----------------------------------------------------------------------------------------------------------
// Assert
//-----------------------------------------------------------------------------------------------------------
act.Should().Throw<XunitException>().WithMessage("Expected enum to equal EnumFour.Three(3) by name, but found EnumOne.Two(3)*");
}

[Fact]
public void When_asserting_members_from_different_char_enum_types_are_equivalent_by_value_it_should_succeed()
{
Expand Down Expand Up @@ -3830,12 +3846,17 @@ internal enum EnumTwo
Two = 3
}

internal enum EnumeThree
internal enum EnumThree
{
ValueZero = 0,
Two = 3
}

internal enum EnumFour
{
Three = 3
}

internal class ClassWithEnumCharOne
{
public EnumCharOne Enum { get; set; }
Expand All @@ -3858,7 +3879,12 @@ internal class ClassWithEnumTwo

internal class ClassWithEnumThree
{
public EnumeThree Enum { get; set; }
public EnumThree Enum { get; set; }
}

internal class ClassWithEnumFour
{
public EnumFour Enum { get; set; }
}

internal class ClassWithNoMembers
Expand Down
9 changes: 5 additions & 4 deletions Tests/Shared.Specs/EnumAssertionSpecs.cs
Expand Up @@ -51,14 +51,15 @@ public void When_both_enums_are_equal_and_of_different_underlying_types_it_shoul
public void When_both_enums_are_large_and_not_equal_it_should_throw()
{
// Arrange
krajek marked this conversation as resolved.
Show resolved Hide resolved
var enumOne = EnumLong.Int64LessOne;
var enumTwo = EnumULong.UInt64Max;
var subjectEnum = EnumLong.Int64LessOne;
var expectedEnum = EnumULong.UInt64Max;

// Act
Action act = () => enumOne.Should().BeEquivalentTo(enumTwo);
Action act = () => subjectEnum.Should().BeEquivalentTo(expectedEnum, "comparing enums should throw");

// Assert
act.Should().Throw<XunitException>();
act.Should().Throw<XunitException>()
.WithMessage($"Expected enum to equal EnumULong.UInt64Max({(UInt64)EnumULong.UInt64Max}) by value because comparing enums should throw, but found EnumLong.Int64LessOne({(Int64)EnumLong.Int64LessOne})*");
}
}
}