diff --git a/Src/FluentAssertions/Common/TypeDescriptionUtility.cs b/Src/FluentAssertions/Common/TypeDescriptionUtility.cs new file mode 100644 index 0000000000..7313a6d908 --- /dev/null +++ b/Src/FluentAssertions/Common/TypeDescriptionUtility.cs @@ -0,0 +1,30 @@ +using System; + +namespace FluentAssertions.Common +{ + internal static class TypeDescriptionUtility + { + public static string GetDescriptionOfObjectType(object obj) + { + return (obj is null) ? "" : GetTypeDescription(obj.GetType(), describeValue: true); + } + + public static string GetTypeDescription(Type type) + => GetTypeDescription(type, describeValue: false); + + private static string GetTypeDescription(Type type, bool describeValue) + { + if ((type.Namespace == "System.Linq") && type.IsGenericType) + { + return "an anonymous iterator from a LINQ expression with element type " + type.GetGenericArguments()[0].FullName; + } + else + { + return + describeValue + ? (type.IsValueType ? "a value of type " : "an instance of ") + type.FullName + : type.FullName; + } + } + } +} diff --git a/Src/FluentAssertions/DataColumnCollectionAssertionExtensions.cs b/Src/FluentAssertions/DataColumnCollectionAssertionExtensions.cs index 8cc8362408..7f7e303aa4 100644 --- a/Src/FluentAssertions/DataColumnCollectionAssertionExtensions.cs +++ b/Src/FluentAssertions/DataColumnCollectionAssertionExtensions.cs @@ -48,8 +48,8 @@ public static class DataColumnCollectionAssertionExtensions .BecauseOf(because, becauseArgs) .FailWith( "Invalid expectation: Expected {context:column collection} to refer to an instance of " + - "DataColumnCollection{reason}, but found {0}.", - assertion.Subject); + "DataColumnCollection{reason}, but found " + + TypeDescriptionUtility.GetDescriptionOfObjectType(assertion.Subject) + "."); } return new AndConstraint>(assertion); @@ -89,8 +89,8 @@ public static class DataColumnCollectionAssertionExtensions .BecauseOf(because, becauseArgs) .FailWith( "Invalid expectation: Expected {context:column collection} to refer to a different instance of " + - "DataColumnCollection{reason}, but found {0}.", - assertion.Subject); + "DataColumnCollection{reason}, but found " + + TypeDescriptionUtility.GetDescriptionOfObjectType(assertion.Subject) + "."); } return new AndConstraint>(assertion); diff --git a/Src/FluentAssertions/DataRowCollectionAssertionExtensions.cs b/Src/FluentAssertions/DataRowCollectionAssertionExtensions.cs index ea5e745ff3..b966bfba3f 100644 --- a/Src/FluentAssertions/DataRowCollectionAssertionExtensions.cs +++ b/Src/FluentAssertions/DataRowCollectionAssertionExtensions.cs @@ -46,8 +46,8 @@ public static class DataRowCollectionAssertionExtensions .BecauseOf(because, becauseArgs) .FailWith( "Invalid expectation: Expected {context:column collection} to refer to an instance of " + - "DataRowCollection{reason}, but found {0}.", - assertion.Subject); + "DataRowCollection{reason}, but found " + + TypeDescriptionUtility.GetDescriptionOfObjectType(assertion.Subject) + "."); } return new AndConstraint>(assertion); @@ -84,8 +84,8 @@ public static class DataRowCollectionAssertionExtensions .BecauseOf(because, becauseArgs) .FailWith( "Invalid expectation: Expected {context:column collection} to refer to a different instance of " + - "DataRowCollection{reason}, but found {0}.", - assertion.Subject); + "DataRowCollection{reason}, but found " + + TypeDescriptionUtility.GetDescriptionOfObjectType(assertion.Subject) + "."); } return new AndConstraint>(assertion); diff --git a/Src/FluentAssertions/DataTableCollectionAssertionExtensions.cs b/Src/FluentAssertions/DataTableCollectionAssertionExtensions.cs index 76cdf9ea64..5c30a822b2 100644 --- a/Src/FluentAssertions/DataTableCollectionAssertionExtensions.cs +++ b/Src/FluentAssertions/DataTableCollectionAssertionExtensions.cs @@ -43,8 +43,8 @@ public static class DataTableCollectionAssertionExtensions .BecauseOf(because, becauseArgs) .FailWith( "Invalid expectation: Expected {context:column collection} to refer to an instance of " + - "DataTableCollection{reason}, but found {0}.", - assertion.Subject); + "DataTableCollection{reason}, but found " + + TypeDescriptionUtility.GetDescriptionOfObjectType(assertion.Subject) + "."); } return new AndConstraint>(assertion); @@ -81,8 +81,8 @@ public static class DataTableCollectionAssertionExtensions .BecauseOf(because, becauseArgs) .FailWith( "Invalid expectation: Expected {context:column collection} to refer to a different instance of " + - "DataTableCollection{reason}, but found {0}.", - assertion.Subject); + "DataTableCollection{reason}, but found " + + TypeDescriptionUtility.GetDescriptionOfObjectType(assertion.Subject) + "."); } return new AndConstraint>(assertion); diff --git a/Src/FluentAssertions/Types/TypeSelectorAssertions.cs b/Src/FluentAssertions/Types/TypeSelectorAssertions.cs index 9e99e15a3c..048ea60b7d 100644 --- a/Src/FluentAssertions/Types/TypeSelectorAssertions.cs +++ b/Src/FluentAssertions/Types/TypeSelectorAssertions.cs @@ -466,15 +466,10 @@ public AndConstraint NotBeUnderNamespace(string @namespa private static string GetDescriptionsFor(IEnumerable types) { - IEnumerable descriptions = types.Select(type => GetDescriptionFor(type)); + IEnumerable descriptions = types.Select(type => TypeDescriptionUtility.GetTypeDescription(type)); return string.Join(Environment.NewLine, descriptions); } - private static string GetDescriptionFor(Type type) - { - return type.ToString(); - } - /// public override bool Equals(object obj) => throw new NotSupportedException("Calling Equals on Assertion classes is not supported."); diff --git a/Tests/FluentAssertions.Specs/Common/TypeDescriptionUtilitySpecs.cs b/Tests/FluentAssertions.Specs/Common/TypeDescriptionUtilitySpecs.cs new file mode 100644 index 0000000000..16a67c5f35 --- /dev/null +++ b/Tests/FluentAssertions.Specs/Common/TypeDescriptionUtilitySpecs.cs @@ -0,0 +1,67 @@ +using System.Collections.Generic; +using System.Linq; + +using FluentAssertions.Common; + +using Xunit; + +namespace FluentAssertions.Specs +{ + public class TypeDescriptionUtilitySpecs + { + [Fact] + public void When_object_is_null_it_should_work() + { + // Act & Assert + TypeDescriptionUtility.GetDescriptionOfObjectType(null).Should().Be(""); + } + + [Fact] + public void When_object_is_value_type_it_should_work() + { + // Act & Assert + TypeDescriptionUtility.GetDescriptionOfObjectType(37).Should().Be("a value of type System.Int32"); + } + + [Fact] + public void When_object_is_reference_type_it_should_work() + { + // Act & Assert + TypeDescriptionUtility.GetDescriptionOfObjectType(new object()).Should().Be("an instance of System.Object"); + } + + [Fact] + public void When_object_is_generic_value_type_it_should_work() + { + // Arrange + var box = new Box() { Value = 37 }; + + // Act & Assert + TypeDescriptionUtility.GetDescriptionOfObjectType(box).Should() + .Match("a value of type *+Box`1[[System.Int32*]]"); + } + + [Fact] + public void When_object_is_generic_reference_type_it_should_work() + { + // Act & Assert + TypeDescriptionUtility.GetDescriptionOfObjectType(new List()).Should() + .Match("an instance of System.Collections.Generic.List`1[[System.Int32*]]"); + } + + [Fact] + public void When_object_is_LINQ_anonymous_iterator_type_it_should_work() + { + // Arrange + var value = new[] { 1, 2, 3 }.Select(x => 2 * x); + + TypeDescriptionUtility.GetDescriptionOfObjectType(value).Should().Be( + "an anonymous iterator from a LINQ expression with element type System.Int32"); + } + + private struct Box + { + public T Value { get; set; } + } + } +} diff --git a/docs/_pages/releases.md b/docs/_pages/releases.md index ff728d4bb2..443f42bc9a 100644 --- a/docs/_pages/releases.md +++ b/docs/_pages/releases.md @@ -23,6 +23,7 @@ sidebar: * Added `NotBe` for nullable boolean values - [#1865](https://github.com/fluentassertions/fluentassertions/pull/1865) * Added a new overload to `MatchRegex()` to assert on the number of regex matches - [#1869](https://github.com/fluentassertions/fluentassertions/pull/1869) * Added difference to numeric assertion failure messages - [#1859](https://github.com/fluentassertions/fluentassertions/pull/1859) +* Improved the formatting of data type names, especially with regard to LINQ results - [#1895](https://github.com/fluentassertions/fluentassertions/1895) ### Fixes * `EnumAssertions.Be` did not determine the caller name - [#1835](https://github.com/fluentassertions/fluentassertions/pull/1835)