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
70 changes: 62 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,63 @@ 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 = GetDisplayNameForEnumComparison(context.Subject, subjectsUnderlyingValue);
string expectationName = GetDisplayNameForEnumComparison(context.Expectation, expectationsUnderlyingValue);

return new FailReason($"Expected {{context:enum}} to equal {expectationName} by value{{reason}}, but found {subjectsName}.");
});
}

private static void HandleByName(IEquivalencyValidationContext context)
{
string subject = context.Subject?.ToString();
string expected = context.Expectation.ToString();

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

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

private static string GetDisplayNameForEnumComparison(object o, decimal? v)
{
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 = v.Value.ToString(CultureInfo.InvariantCulture);
return $"{typePart}.{namePart}({valuePart})";
}

return v.Value.ToString(CultureInfo.InvariantCulture);
}

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*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
75 changes: 71 additions & 4 deletions Tests/Shared.Specs/EnumAssertionSpecs.cs
Expand Up @@ -22,43 +22,110 @@ public class EnumAssertionSpecs
[Fact]
public void When_both_enums_are_equal_and_greater_than_max_long_it_should_not_throw()
{
//-----------------------------------------------------------------------------------------------------------
// Arrange
//-----------------------------------------------------------------------------------------------------------
var enumOne = EnumULong.UInt64Max;
var enumTwo = EnumULong.UInt64Max;

//-----------------------------------------------------------------------------------------------------------
// Act
//-----------------------------------------------------------------------------------------------------------
Action act = () => enumOne.Should().BeEquivalentTo(enumTwo);

//-----------------------------------------------------------------------------------------------------------
// Assert
//-----------------------------------------------------------------------------------------------------------
act.Should().NotThrow();
}

[Fact]
public void When_both_enums_are_equal_and_of_different_underlying_types_it_should_not_throw()
{
//-----------------------------------------------------------------------------------------------------------
// Arrange
//-----------------------------------------------------------------------------------------------------------
var enumOne = EnumLong.Int64Max;
var enumTwo = EnumULong.Int64Max;

//-----------------------------------------------------------------------------------------------------------
// Act
//-----------------------------------------------------------------------------------------------------------
Action act = () => enumOne.Should().BeEquivalentTo(enumTwo);

//-----------------------------------------------------------------------------------------------------------
// Assert
//-----------------------------------------------------------------------------------------------------------
act.Should().NotThrow();
}

[Fact]
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>()
#if NETCOREAPP1_1
.WithMessage($"Expected enum to equal EnumULong.UInt64Max({(UInt64)EnumULong.UInt64Max}) by value because comparing enums should throw, but found EnumLong.Int64LessOne({(Int64)EnumLong.Int64LessOne})*");
#else
.WithMessage($"Expected subjectEnum to equal EnumULong.UInt64Max({(UInt64)EnumULong.UInt64Max}) by value because comparing enums should throw, but found EnumLong.Int64LessOne({(Int64)EnumLong.Int64LessOne})*");
#endif
}

[Fact]
public void When_subject_is_null_and_enum_has_some_value_it_should_throw()
{
//-----------------------------------------------------------------------------------------------------------
// Arrange
//-----------------------------------------------------------------------------------------------------------
object subject = null;
object expectedEnum = EnumULong.UInt64Max;

//-----------------------------------------------------------------------------------------------------------
// Act
//-----------------------------------------------------------------------------------------------------------
// ReSharper disable once ExpressionIsAlwaysNull
Action act = () => subject.Should().BeEquivalentTo(expectedEnum, x => x.ComparingEnumsByName(), "comparing enums should throw");

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

[Fact]
public void When_expectation_is_null_and_subject_enum_has_some_value_it_should_throw()
{
//-----------------------------------------------------------------------------------------------------------
// Arrange
//-----------------------------------------------------------------------------------------------------------
object subjectEnum = EnumULong.UInt64Max;
object expected = null;

//-----------------------------------------------------------------------------------------------------------
// Act
//-----------------------------------------------------------------------------------------------------------
// ReSharper disable once ExpressionIsAlwaysNull
Action act = () => subjectEnum.Should().BeEquivalentTo(expected, x => x.ComparingEnumsByName(), "comparing enums should throw");

//-----------------------------------------------------------------------------------------------------------
// Assert
act.Should().Throw<XunitException>();
//-----------------------------------------------------------------------------------------------------------
act.Should().Throw<XunitException>()
.WithMessage("Expected*to be <null>, but found UInt64Max*");
}
}
}