Skip to content

Commit

Permalink
Added utility class TypeDescriptionUtility.cs, along with unit tests …
Browse files Browse the repository at this point in the history
…in TypeDescriptionUtilitySpecs.cs.

Refactored AssertionScope.FailWith to produce new utility method AssertionScope.FormatFailureMessage.
Updated DataTableCollectionAssertionExtensions.cs, DataColumnCollectionAssertionExtensions.cs and DataRowCollectionAssertionExtensions.cs to throw InvalidOperationException when BeSameAs or NotBeSameAs is called with a subject of the wrong type, using the new AssertionScope.FormatFailureMessage method to give the exception an appropriate message.
Updated DataTableCollectionAssertionSpecs.cs, DataColumnCollectionAssertionSpecs.cs and DataRowCollectionAssertionSpecs.cs to account for the change in the exception type & message in the When_generic_collection_is_tested_against_typed_collection_it_should_fail test methods.
  • Loading branch information
logiclrd committed Mar 14, 2022
1 parent 5c3204f commit 3e43e31
Show file tree
Hide file tree
Showing 9 changed files with 213 additions and 28 deletions.
33 changes: 27 additions & 6 deletions Src/FluentAssertions/DataColumnCollectionAssertionExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.Linq;

using FluentAssertions.Collections;
Expand All @@ -25,6 +27,9 @@ public static class DataColumnCollectionAssertionExtensions
this GenericCollectionAssertions<DataColumn> assertion, DataColumnCollection expected, string because = "",
params object[] becauseArgs)
{
Guard.ThrowIfArgumentIsNull(
expected, nameof(expected), "Cannot verify same reference against a <null> collection (use BeNull instead?).");

if (assertion.Subject is ReadOnlyNonGenericCollectionWrapper<DataColumnCollection, DataColumn> wrapper)
{
var actualSubject = wrapper.UnderlyingCollection;
Expand All @@ -39,12 +44,14 @@ public static class DataColumnCollectionAssertionExtensions
}
else
{
Execute.Assertion
.UsingLineBreaks
string exceptionMessage = AssertionScope.Current
.BecauseOf(because, becauseArgs)
.FailWith(
"Expected {context:column collection} to refer to DataColumnCollection{reason}, but found {0} (different type).",
assertion.Subject);
.FormatFailureMessage(
"Invalid expectation: Expected {context:column collection} to refer to an instance of " +
"DataColumnCollection{reason}, but found " +
TypeDescriptionUtility.GetDescriptionOfObjectType(assertion.Subject)) + ".";

throw new InvalidOperationException(exceptionMessage);
}

return new AndConstraint<GenericCollectionAssertions<DataColumn>>(assertion);
Expand All @@ -65,6 +72,9 @@ public static class DataColumnCollectionAssertionExtensions
this GenericCollectionAssertions<DataColumn> assertion, DataColumnCollection unexpected, string because = "",
params object[] becauseArgs)
{
Guard.ThrowIfArgumentIsNull(
unexpected, nameof(unexpected), "Cannot verify same reference against a <null> collection (use NotBeNull instead?).");

if (assertion.Subject is ReadOnlyNonGenericCollectionWrapper<DataColumnCollection, DataColumn> wrapper)
{
var actualSubject = wrapper.UnderlyingCollection;
Expand All @@ -75,6 +85,17 @@ public static class DataColumnCollectionAssertionExtensions
.BecauseOf(because, becauseArgs)
.FailWith("Did not expect {context:column collection} to refer to {0}{reason}.", unexpected);
}
else
{
string exceptionMessage = AssertionScope.Current
.BecauseOf(because, becauseArgs)
.FormatFailureMessage(
"Invalid expectation: Expected {context:column collection} to refer to a different instance of " +
"DataColumnCollection{reason}, but found " +
TypeDescriptionUtility.GetDescriptionOfObjectType(assertion.Subject)) + ".";

throw new InvalidOperationException(exceptionMessage);
}

return new AndConstraint<GenericCollectionAssertions<DataColumn>>(assertion);
}
Expand Down
23 changes: 18 additions & 5 deletions Src/FluentAssertions/DataRowCollectionAssertionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,14 @@ public static class DataRowCollectionAssertionExtensions
}
else
{
Execute.Assertion
.UsingLineBreaks
string exceptionMessage = AssertionScope.Current
.BecauseOf(because, becauseArgs)
.FailWith(
"Expected {context:row collection} to refer to DataRowCollection{reason}, but found {0} (different type).",
assertion.Subject);
.FormatFailureMessage(
"Invalid expectation: Expected {context:column collection} to refer to an instance of " +
"DataRowCollection{reason}, but found " +
TypeDescriptionUtility.GetDescriptionOfObjectType(assertion.Subject)) + ".";

throw new InvalidOperationException(exceptionMessage);
}

return new AndConstraint<GenericCollectionAssertions<DataRow>>(assertion);
Expand Down Expand Up @@ -78,6 +80,17 @@ public static class DataRowCollectionAssertionExtensions
.BecauseOf(because, becauseArgs)
.FailWith("Did not expect {context:row collection} to refer to {0}{reason}.", unexpected);
}
else
{
string exceptionMessage = AssertionScope.Current
.BecauseOf(because, becauseArgs)
.FormatFailureMessage(
"Invalid expectation: Expected {context:column collection} to refer to a difference instance of " +
"DataRowCollection{reason}, but found " +
TypeDescriptionUtility.GetDescriptionOfObjectType(assertion.Subject)) + ".";

throw new InvalidOperationException(exceptionMessage);
}

return new AndConstraint<GenericCollectionAssertions<DataRow>>(assertion);
}
Expand Down
26 changes: 20 additions & 6 deletions Src/FluentAssertions/DataTableCollectionAssertionExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Data;
using System;
using System.Data;
using System.Linq;

using FluentAssertions.Collections;
Expand Down Expand Up @@ -38,12 +39,14 @@ public static class DataTableCollectionAssertionExtensions
}
else
{
Execute.Assertion
.UsingLineBreaks
string exceptionMessage = AssertionScope.Current
.BecauseOf(because, becauseArgs)
.FailWith(
"Expected {context:table collection} to refer to DataTableCollection{reason}, but found {0} (different type).",
assertion.Subject);
.FormatFailureMessage(
"Invalid expectation: Expected {context:column collection} to refer to an instance of " +
"DataTableCollection{reason}, but found " +
TypeDescriptionUtility.GetDescriptionOfObjectType(assertion.Subject)) + ".";

throw new InvalidOperationException(exceptionMessage);
}

return new AndConstraint<GenericCollectionAssertions<DataTable>>(assertion);
Expand Down Expand Up @@ -74,6 +77,17 @@ public static class DataTableCollectionAssertionExtensions
.BecauseOf(because, becauseArgs)
.FailWith("Did not expect {context:table collection} to refer to {0}{reason}.", unexpected);
}
else
{
string exceptionMessage = AssertionScope.Current
.BecauseOf(because, becauseArgs)
.FormatFailureMessage(
"Invalid expectation: Expected {context:column collection} to refer to a difference instance of " +
"DataTableCollection{reason}, but found " +
TypeDescriptionUtility.GetDescriptionOfObjectType(assertion.Subject)) + ".";

throw new InvalidOperationException(exceptionMessage);
}

return new AndConstraint<GenericCollectionAssertions<DataTable>>(assertion);
}
Expand Down
16 changes: 11 additions & 5 deletions Src/FluentAssertions/Execution/AssertionScope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -245,15 +245,21 @@ public Continuation FailWith(Func<FailReason> failReasonFunc)
{
return FailWith(() =>
{
string localReason = reason?.Invoke() ?? string.Empty;
var messageBuilder = new MessageBuilder(formattingOptions);
string identifier = GetIdentifier();
FailReason failReason = failReasonFunc();
string result = messageBuilder.Build(failReason.Message, failReason.Args, localReason, contextData, identifier, fallbackIdentifier);
return result;
return FormatFailureMessage(failReason.Message, failReason.Args);
});
}

internal string FormatFailureMessage(string message, params object[] args)
{
string localReason = reason?.Invoke() ?? string.Empty;
var messageBuilder = new MessageBuilder(formattingOptions);
string identifier = GetIdentifier();
string result = messageBuilder.Build(message, args, localReason, contextData, identifier, fallbackIdentifier);
return result;
}

internal Continuation FailWithPreFormatted(string formattedFailReason) =>
FailWith(() => formattedFailReason);

Expand Down
20 changes: 20 additions & 0 deletions Src/FluentAssertions/TypeDescriptionUtility.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;

namespace FluentAssertions
{
internal static class TypeDescriptionUtility
{
public static string GetDescriptionOfObjectType(object obj)
{
return (obj is null) ? "<null>" : GetTypeDescription(obj.GetType());
}

public static string GetTypeDescription(Type type)
{
return
((type.Namespace == "System.Linq") && type.IsGenericType)
? "an anonymous iterator from a LINQ expression with element type " + type.GetGenericArguments()[0].FullName
: (type.IsValueType ? "a value of type " : "an instance of ") + type.FullName;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,9 @@ public void When_generic_collection_is_tested_against_typed_collection_it_should
() => genericCollection.Should().BeSameAs(columnCollection, because: "we {0}", "care");

// Assert
action.Should().Throw<XunitException>().WithMessage(
"Expected genericCollection to refer to DataColumnCollection because we care, but found * (different type).");
action.Should().Throw<InvalidOperationException>().WithMessage(
"Invalid expectation: Expected genericCollection to refer to an instance of DataColumnCollection " +
"because we care, but found *.");
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,9 @@ public void When_generic_collection_is_tested_against_typed_collection_it_should
() => genericCollection.Should().BeSameAs(rowCollection, because: "we {0}", "care");

// Assert
action.Should().Throw<XunitException>().WithMessage(
"Expected genericCollection to refer to DataRowCollection because we care, but found * (different type).");
action.Should().Throw<InvalidOperationException>().WithMessage(
"Invalid expectation: Expected genericCollection to refer to an instance of DataRowCollection " +
"because we care, but found *.");
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,9 @@ public void When_generic_collection_is_tested_against_typed_collection_it_should
() => genericCollection.Should().BeSameAs(tableCollection, because: "we {0}", "care");

// Assert
action.Should().Throw<XunitException>().WithMessage(
"Expected genericCollection to refer to DataTableCollection because we care, but found * (different type).");
action.Should().Throw<InvalidOperationException>().WithMessage(
"Invalid expectation: Expected genericCollection to refer to an instance of DataTableCollection " +
"because we care, but found *.");
}
}

Expand Down
108 changes: 108 additions & 0 deletions Tests/FluentAssertions.Specs/TypeDescriptionUtilitySpecs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
using System.Collections.Generic;
using System.Linq;

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("<null>");
}

[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<int>() { 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<int>()).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");
}

[Fact]
public void When_type_is_value_type_it_should_work()
{
// Act & Assert
TypeDescriptionUtility.GetTypeDescription(typeof(int)).Should().Be("a value of type System.Int32");
}

[Fact]
public void When_type_is_reference_type_it_should_work()
{
// Act & Assert
TypeDescriptionUtility.GetTypeDescription(typeof(object)).Should().Be("an instance of System.Object");
}

[Fact]
public void When_type_is_generic_value_type_it_should_work()
{
// Arrange
var value = new Box<int>() { Value = 37 };

// Act & Assert
TypeDescriptionUtility.GetTypeDescription(value.GetType()).Should()
.Match("a value of type *+Box`1[[System.Int32*]]");
}

[Fact]
public void When_type_is_generic_reference_type_it_should_work()
{
// Act & Assert
TypeDescriptionUtility.GetTypeDescription(typeof(List<int>)).Should()
.Match("an instance of System.Collections.Generic.List`1[[System.Int32*]]");
}

[Fact]
public void When_type_is_LINQ_anonymous_iterator_type_it_should_work()
{
// Arrange
var value = new[] { 1, 2, 3 }.Select(x => 2 * x);

TypeDescriptionUtility.GetTypeDescription(value.GetType()).Should().Be(
"an anonymous iterator from a LINQ expression with element type System.Int32");
}

private struct Box<T>
{
public T Value { get; set; }
}
}
}

0 comments on commit 3e43e31

Please sign in to comment.