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

Add collection assertion ContainEquivalentOf #950

Merged
merged 7 commits into from Dec 15, 2018
92 changes: 92 additions & 0 deletions Src/FluentAssertions/Collections/CollectionAssertions.cs
Expand Up @@ -462,6 +462,98 @@ public AndConstraint<TAssertions> BeEquivalentTo(params object[] expectations)
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);
matthiaslischka marked this conversation as resolved.
Show resolved Hide resolved
}
}

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
246 changes: 245 additions & 1 deletion Tests/Shared.Specs/CollectionAssertionSpecs.cs
@@ -1,7 +1,6 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Xunit;
using Xunit.Sdk;
Expand Down Expand Up @@ -2337,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';
matthiaslischka marked this conversation as resolved.
Show resolved Hide resolved

//-----------------------------------------------------------------------------------------------------------
// 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