Skip to content

Commit

Permalink
Improve enum comparison assert messages (#923)
Browse files Browse the repository at this point in the history
  • Loading branch information
krajek authored and dennisdoomen committed Sep 27, 2018
1 parent 544f500 commit bbf703e
Show file tree
Hide file tree
Showing 3 changed files with 168 additions and 21 deletions.
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()
{
//-----------------------------------------------------------------------------------------------------------
// 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
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*");
}
}
}

0 comments on commit bbf703e

Please sign in to comment.