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

Rework EnumAssertions #1479

Merged
merged 2 commits into from
Feb 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 0 additions & 7 deletions Src/FluentAssertions/AssertionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -130,13 +130,6 @@ public static ExecutionTime ExecutionTime(this Func<Task> action)
return new ExecutionTime(action);
}

/// <summary>
/// Returns an <see cref="EnumAssertions"/> object that can be used to assert the
/// current <see cref="Enum"/>.
/// </summary>
public static EnumAssertions Should(this Enum @enum)
=> new EnumAssertions(@enum);

/// <summary>
/// Returns an <see cref="ExecutionTimeAssertions"/> object that can be used to assert the
/// current <see cref="FluentAssertions.Specialized.ExecutionTime"/>.
Expand Down
8 changes: 3 additions & 5 deletions Src/FluentAssertions/Common/ObjectExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ public static bool IsSameOrEqualTo(this object actual, object expected)
Type actualType = actual.GetType();

return actualType != expectedType
&& (actual.IsNumericType() || actualType.IsEnum)
&& (expected.IsNumericType() || expectedType.IsEnum)
&& actual.IsNumericType()
&& expected.IsNumericType()
&& CanConvert(actual, expected, actualType, expectedType)
&& CanConvert(expected, actual, expectedType, actualType);
}
Expand All @@ -55,9 +55,7 @@ private static bool CanConvert(object source, object target, Type sourceType, Ty

private static object ConvertTo(this object source, Type targetType)
{
return targetType.IsEnum
? Enum.ToObject(targetType, source)
: Convert.ChangeType(source, targetType, CultureInfo.InvariantCulture);
return Convert.ChangeType(source, targetType, CultureInfo.InvariantCulture);
}

private static bool IsNumericType(this object obj)
Expand Down
36 changes: 36 additions & 0 deletions Src/FluentAssertions/EnumAssertionsExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using FluentAssertions.Primitives;

namespace FluentAssertions
{
/// <summary>
/// Contains an extension method for custom assertions in unit tests related to Enum objects.
/// </summary>
[DebuggerNonUserCode]
public static class EnumAssertionsExtensions
{
/// <summary>
/// Returns an <see cref="EnumAssertions{TEnum, TAssertions}"/> object that can be used to assert the
/// current <typeparamref name="TEnum"/>.
/// </summary>
[Pure]
public static EnumAssertions<TEnum> Should<TEnum>(this TEnum @enum)
where TEnum : struct, Enum
{
return new EnumAssertions<TEnum>(@enum);
}

/// <summary>
/// Returns an <see cref="EnumAssertions{TEnum, TAssertions}"/> object that can be used to assert the
/// current <typeparamref name="TEnum"/>.
/// </summary>
[Pure]
public static NullableEnumAssertions<TEnum> Should<TEnum>(this TEnum? @enum)
where TEnum : struct, Enum
{
return new NullableEnumAssertions<TEnum>(@enum);
}
}
}
32 changes: 18 additions & 14 deletions Src/FluentAssertions/Equivalency/EnumEqualityStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,9 @@ public class EnumEqualityStep : IEquivalencyStep
/// </summary>
public bool CanHandle(IEquivalencyValidationContext context, IEquivalencyAssertionOptions config)
{
Type subjectType = config.GetExpectationType(context.RuntimeType, context.CompileTimeType);
Type expectationType = config.GetExpectationType(context.RuntimeType, context.CompileTimeType);

return (subjectType?.IsEnum == true) ||
(context.Expectation?.GetType().IsEnum == true);
return expectationType.IsEnum;
}

/// <summary>
Expand All @@ -34,6 +33,16 @@ public bool CanHandle(IEquivalencyValidationContext context, IEquivalencyAsserti
public bool Handle(IEquivalencyValidationContext context, IEquivalencyValidator parent,
IEquivalencyAssertionOptions config)
{
Execute.Assertion
.ForCondition(context.Subject?.GetType().IsEnum == true)
.FailWith(() =>
{
decimal? expectationsUnderlyingValue = ExtractDecimal(context.Expectation);
string expectationName = GetDisplayNameForEnumComparison(context.Expectation, expectationsUnderlyingValue);

return new FailReason($"Expected {{context:enum}} to be equivalent to {expectationName}{{reason}}, but found {{0}}.", context.Subject);
});

switch (config.EnumEquivalencyHandling)
{
case EnumEquivalencyHandling.ByValue:
Expand Down Expand Up @@ -69,7 +78,7 @@ private static void HandleByValue(IEquivalencyValidationContext context)

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

Execute.Assertion
Expand All @@ -90,18 +99,13 @@ private static string GetDisplayNameForEnumComparison(object o, decimal? v)
{
if (o is null || v is null)
{
return "null";
}

if (o.GetType().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 "<null>";
}

return v.Value.ToString(CultureInfo.InvariantCulture);
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)
Expand Down
4 changes: 2 additions & 2 deletions Src/FluentAssertions/Equivalency/EnumerableEquivalencyStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ public class EnumerableEquivalencyStep : IEquivalencyStep
/// </summary>
public bool CanHandle(IEquivalencyValidationContext context, IEquivalencyAssertionOptions config)
{
Type subjectType = config.GetExpectationType(context.RuntimeType, context.CompileTimeType);
Type expectationType = config.GetExpectationType(context.RuntimeType, context.CompileTimeType);

return IsCollection(subjectType);
return IsCollection(expectationType);
}

/// <summary>
Expand Down
31 changes: 31 additions & 0 deletions Src/FluentAssertions/Formatting/EnumValueFormatter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;
using System.Globalization;

namespace FluentAssertions.Formatting
{
public class EnumValueFormatter : IValueFormatter
{
/// <summary>
/// Indicates whether the current <see cref="IValueFormatter"/> can handle the specified <paramref name="value"/>.
/// </summary>
/// <param name="value">The value for which to create a <see cref="string"/>.</param>
/// <returns>
/// <c>true</c> if the current <see cref="IValueFormatter"/> can handle the specified value; otherwise, <c>false</c>.
/// </returns>
public virtual bool CanHandle(object value)
{
return value is Enum;
}

/// <inheritdoc />
public string Format(object value, FormattingContext context, FormatChild formatChild)
{
string typePart = value.GetType().Name;
string namePart = value.ToString().Replace(", ", "|", StringComparison.Ordinal);
string valuePart = Convert.ToDecimal(value, CultureInfo.InvariantCulture)
.ToString(CultureInfo.InvariantCulture);

return $"{typePart}.{namePart} {{value: {valuePart}}}";
}
}
}
1 change: 1 addition & 0 deletions Src/FluentAssertions/Formatting/Formatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public static class Formatter
new ExceptionValueFormatter(),
new MultidimensionalArrayFormatter(),
new EnumerableValueFormatter(),
new EnumValueFormatter(),
new DefaultValueFormatter()
};

Expand Down
4 changes: 2 additions & 2 deletions Src/FluentAssertions/Primitives/DateTimeAssertions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -911,12 +911,12 @@ public AndConstraint<TAssertions> BeIn(DateTimeKind expectedKind, string because
{
Execute.Assertion
.BecauseOf(because, becauseArgs)
.WithExpectation("Expected {context:the date and time} to be in {0}{reason}", expectedKind)
.WithExpectation("Expected {context:the date and time} to be in " + expectedKind.ToString() + "{reason}")
.ForCondition(Subject.HasValue)
.FailWith(", but found a <null> DateTime.")
.Then
.ForCondition(Subject.Value.Kind == expectedKind)
.FailWith(", but found {0}.", Subject.Value.Kind)
.FailWith(", but found " + Subject.Value.Kind.ToString() + ".")
.Then
.ClearExpectation();

Expand Down