Skip to content

Commit

Permalink
Merge pull request #1479 from jnyrup/EnumAssertions
Browse files Browse the repository at this point in the history
  • Loading branch information
jnyrup committed Feb 9, 2021
2 parents 02ba992 + deca4db commit b25ccc2
Show file tree
Hide file tree
Showing 25 changed files with 1,327 additions and 313 deletions.
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

0 comments on commit b25ccc2

Please sign in to comment.