Skip to content

Commit

Permalink
Add collection assertion ContainEquivalentOf (#950)
Browse files Browse the repository at this point in the history
  • Loading branch information
matthiaslischka authored and dennisdoomen committed Dec 15, 2018
1 parent f92c994 commit 0702ef4
Show file tree
Hide file tree
Showing 3 changed files with 342 additions and 0 deletions.
92 changes: 92 additions & 0 deletions Src/FluentAssertions/Collections/CollectionAssertions.cs
Expand Up @@ -534,6 +534,98 @@ public AndConstraint<TAssertions> BeEquivalentTo(IEnumerable expectation, string
return new AndConstraint<TAssertions>((TAssertions)this);
}

/// <summary>
/// Asserts that a collection of objects contains at least one object equivalent to another object.
/// </summary>
/// <remarks>
/// Objects within the collection are equivalent to the expected object when both object graphs have equally named properties with the same
/// value, irrespective of the type of those objects. Two properties are also equal if one type can be converted to another
/// and the result is equal.
/// Notice that actual behavior is determined by the global defaults managed by <see cref="AssertionOptions"/>.
/// </remarks>
/// <param name="because">
/// An optional formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the
/// assertion is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically.
/// </param>
/// <param name="becauseArgs">
/// Zero or more objects to format using the placeholders in <see cref="because" />.
/// </param>
public AndConstraint<TAssertions> ContainEquivalentOf<TExpectation>(TExpectation expectation, string because = "",
params object[] becauseArgs)
{
return ContainEquivalentOf(expectation, config => config, because, becauseArgs);
}

/// <summary>
/// Asserts that a collection of objects contains at least one object equivalent to another object.
/// </summary>
/// <remarks>
/// Objects within the collection are equivalent to the expected object when both object graphs have equally named properties with the same
/// value, irrespective of the type of those objects. Two properties are also equal if one type can be converted to another
/// and the result is equal.
/// Notice that actual behavior is determined by the global defaults managed by <see cref="AssertionOptions"/>.
/// </remarks>
/// <param name="config">
/// A reference to the <see cref="EquivalencyAssertionOptions{TSubject}"/> configuration object that can be used
/// to influence the way the object graphs are compared. You can also provide an alternative instance of the
/// <see cref="EquivalencyAssertionOptions{TSubject}"/> class. The global defaults are determined by the
/// <see cref="AssertionOptions"/> class.
/// </param>
/// <param name="because">
/// An optional formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the
/// assertion is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically.
/// </param>
/// <param name="becauseArgs">
/// Zero or more objects to format using the placeholders in <see cref="because" />.
/// </param>
public AndConstraint<TAssertions> ContainEquivalentOf<TExpectation>(TExpectation expectation, Func<EquivalencyAssertionOptions<TExpectation>,
EquivalencyAssertionOptions<TExpectation>> config, string because = "", params object[] becauseArgs)
{
if (ReferenceEquals(Subject, null))
{
Execute.Assertion
.BecauseOf(because, becauseArgs)
.FailWith("Expected {context:collection} to contain equivalent of {0}{reason}, but found <null>.", expectation);
}

IEquivalencyAssertionOptions options = config(AssertionOptions.CloneDefaults<TExpectation>());
IEnumerable<object> actualItems = Subject.Cast<object>();

using (var scope = new AssertionScope())
{
scope.AddReportable("configuration", options.ToString());

foreach (object actualItem in actualItems)
{
var context = new EquivalencyValidationContext
{
Subject = actualItem,
Expectation = expectation,
CompileTimeType = typeof(TExpectation),
Because = because,
BecauseArgs = becauseArgs,
Tracer = options.TraceWriter,
};

var equivalencyValidator = new EquivalencyValidator(options);
equivalencyValidator.AssertEquality(context);

string[] failures = scope.Discard();

if (!failures.Any())
{
return new AndConstraint<TAssertions>((TAssertions)this);
}
}

Execute.Assertion
.BecauseOf(because, becauseArgs)
.FailWith("Expected {context:collection} {0} to contain equivalent of {1}.", Subject, expectation);
}

return new AndConstraint<TAssertions>((TAssertions)this);
}

/// <summary>
/// Asserts that the current collection only contains items that are assignable to the type <typeparamref name="T" />.
/// </summary>
Expand Down
245 changes: 245 additions & 0 deletions Tests/Shared.Specs/CollectionAssertionSpecs.cs
Expand Up @@ -2336,6 +2336,251 @@ public void When_testing_collections_not_to_be_equivalent_against_same_collectio

#endregion

#region Contain Equivalent Of

[Fact]
public void When_collection_contains_object_equal_of_another_it_should_succeed()
{
//-----------------------------------------------------------------------------------------------------------
// Arrange
//-----------------------------------------------------------------------------------------------------------
var item = new Customer { Name = "John" };
IEnumerable collection = new[] { new Customer { Name = "Jane" }, item };

//-----------------------------------------------------------------------------------------------------------
// Act / Assert
//-----------------------------------------------------------------------------------------------------------
collection.Should().ContainEquivalentOf(item);
}

[Fact]
public void When_collection_contains_object_equivalent_of_another_it_should_succeed()
{
//-----------------------------------------------------------------------------------------------------------
// Arrange
//-----------------------------------------------------------------------------------------------------------
IEnumerable collection = new[] { new Customer { Name = "Jane" }, new Customer { Name = "John" } };
var item = new Customer { Name = "John" };

//-----------------------------------------------------------------------------------------------------------
// Act / Assert
//-----------------------------------------------------------------------------------------------------------
collection.Should().ContainEquivalentOf(item);
}

[Fact]
public void When_character_collection_does_contain_equivalent_it_should_succeed()
{
//-----------------------------------------------------------------------------------------------------------
// Arrange
//-----------------------------------------------------------------------------------------------------------
char[] collection = ("abc123ab").ToCharArray();
char item = 'c';

//-----------------------------------------------------------------------------------------------------------
// Act / Assert
//-----------------------------------------------------------------------------------------------------------
collection.Should().ContainEquivalentOf(item);
}

[Fact]
public void When_string_collection_does_contain_same_string_with_other_case_it_should_throw()
{
//-----------------------------------------------------------------------------------------------------------
// Arrange
//-----------------------------------------------------------------------------------------------------------
string[] collection = new[] { "a", "b", "c" };
string item = "C";

//-----------------------------------------------------------------------------------------------------------
// Act
//-----------------------------------------------------------------------------------------------------------
Action act = () => collection.Should().ContainEquivalentOf(item);

//-----------------------------------------------------------------------------------------------------------
// Assert
//-----------------------------------------------------------------------------------------------------------
act.Should().Throw<XunitException>().WithMessage("Expected collection {\"a\", \"b\", \"c\"} to contain equivalent of \"C\".*");
}

[Fact]
public void When_collection_does_not_contain_object_equivalent_of_another_it_should_throw()
{
//-----------------------------------------------------------------------------------------------------------
// Arrange
//-----------------------------------------------------------------------------------------------------------
IEnumerable collection = new[] { 1, 2, 3 };
int item = 4;

//-----------------------------------------------------------------------------------------------------------
// Act
//-----------------------------------------------------------------------------------------------------------
Action act = () => collection.Should().ContainEquivalentOf(item); ;

//-----------------------------------------------------------------------------------------------------------
// Act / Assert
//-----------------------------------------------------------------------------------------------------------
act.Should().Throw<XunitException>().WithMessage("Expected collection {1, 2, 3} to contain equivalent of 4.*");
}

[Fact]
public void When_asserting_collection_to_contain_equivalent_but_collection_is_null_it_should_throw()
{
//-----------------------------------------------------------------------------------------------------------
// Arrange
//-----------------------------------------------------------------------------------------------------------
IEnumerable collection = null;
int expectation = 1;

//-----------------------------------------------------------------------------------------------------------
// Act
//-----------------------------------------------------------------------------------------------------------
Action act = () =>
collection.Should().ContainEquivalentOf(expectation, "because we want to test the behaviour with a null subject");

//-----------------------------------------------------------------------------------------------------------
// Assert
//-----------------------------------------------------------------------------------------------------------
act.Should().Throw<XunitException>().WithMessage(
"Expected collection to contain equivalent of 1 because we want to test the behaviour with a null subject, but found <null>.");
}

[Fact]
public void When_collection_contains_equivalent_null_object_it_should_succeed()
{
//-----------------------------------------------------------------------------------------------------------
// Arrange
//-----------------------------------------------------------------------------------------------------------
IEnumerable collection = new[] { 1, 2, 3, (int?)null };
int? item = null;

//-----------------------------------------------------------------------------------------------------------
// Act
//-----------------------------------------------------------------------------------------------------------
Action act = () => collection.Should().ContainEquivalentOf(item);

//-----------------------------------------------------------------------------------------------------------
// Assert
//-----------------------------------------------------------------------------------------------------------
act.Should().NotThrow();
}

[Fact]
public void When_collection_does_not_contain_equivalent_null_object_it_should_throw()
{
//-----------------------------------------------------------------------------------------------------------
// Arrange
//-----------------------------------------------------------------------------------------------------------
IEnumerable collection = new[] { 1, 2, 3 };
int? item = null;

//-----------------------------------------------------------------------------------------------------------
// Act
//-----------------------------------------------------------------------------------------------------------
Action act = () => collection.Should().ContainEquivalentOf(item);

//-----------------------------------------------------------------------------------------------------------
// Assert
//-----------------------------------------------------------------------------------------------------------
act.Should().Throw<XunitException>().WithMessage("Expected collection {1, 2, 3} to contain equivalent of <null>.*");
}

[Fact]
public void When_empty_collection_does_not_contain_equivalent_it_should_throw()
{
//-----------------------------------------------------------------------------------------------------------
// Arrange
//-----------------------------------------------------------------------------------------------------------
IEnumerable collection = new int[0];
int item = 1;

//-----------------------------------------------------------------------------------------------------------
// Act
//-----------------------------------------------------------------------------------------------------------
Action act = () => collection.Should().ContainEquivalentOf(item);

//-----------------------------------------------------------------------------------------------------------
// Assert
//-----------------------------------------------------------------------------------------------------------
act.Should().Throw<XunitException>().WithMessage("Expected collection {empty} to contain equivalent of 1.*");
}

[Fact]
public void When_collection_does_not_contain_equivalent_because_of_second_property_it_should_throw()
{
//-----------------------------------------------------------------------------------------------------------
// Arrange
//-----------------------------------------------------------------------------------------------------------
IEnumerable subject = new []
{
new Customer
{
Name = "John",
Age = 18
},
new Customer
{
Name = "Jane",
Age = 18
}
};
var item = new Customer { Name = "John", Age = 20 };

//-----------------------------------------------------------------------------------------------------------
// Act
//-----------------------------------------------------------------------------------------------------------
Action act = () => subject.Should().ContainEquivalentOf(item);

//-----------------------------------------------------------------------------------------------------------
// Assert
//-----------------------------------------------------------------------------------------------------------
act.Should().Throw<XunitException>();
}

[Fact]
public void When_collection_does_contain_equivalent_by_including_single_property_it_should_not_throw()
{
//-----------------------------------------------------------------------------------------------------------
// Arrange
//-----------------------------------------------------------------------------------------------------------
IEnumerable collection = new []
{
new Customer
{
Name = "John",
Age = 18
},
new Customer
{
Name = "Jane",
Age = 18
}
};
var item = new Customer { Name = "John", Age = 20 };

//-----------------------------------------------------------------------------------------------------------
// Act / Assert
//-----------------------------------------------------------------------------------------------------------
collection.Should().ContainEquivalentOf(item, options => options.Including(x => x.Name));
}

[Fact]
public void When_collection_contains_object_equivalent_of_boxed_object_it_should_succeed()
{
//-----------------------------------------------------------------------------------------------------------
// Arrange
//-----------------------------------------------------------------------------------------------------------
IEnumerable collection = new[] { 1, 2, 3 };
object boxedValue = 2;

//-----------------------------------------------------------------------------------------------------------
// Act / Assert
//-----------------------------------------------------------------------------------------------------------
collection.Should().ContainEquivalentOf(boxedValue);
}

#endregion

#region Be Subset Of

[Fact]
Expand Down

0 comments on commit 0702ef4

Please sign in to comment.