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
88 changes: 88 additions & 0 deletions Src/FluentAssertions/Collections/CollectionAssertions.cs
Expand Up @@ -462,6 +462,94 @@ public AndConstraint<TAssertions> BeEquivalentTo(params object[] expectations)
return new AndConstraint<TAssertions>((TAssertions)this);
}

/// <summary>
/// Asserts that a collection of objects contains an object equivalent to another object.
matthiaslischka marked this conversation as resolved.
Show resolved Hide resolved
/// </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> ContainEquivalentTo<TExpectation>(TExpectation expectation, string because = "",
matthiaslischka marked this conversation as resolved.
Show resolved Hide resolved
params object[] becauseArgs)
{
return ContainEquivalentTo(expectation, config => config, because, becauseArgs);
}

/// <summary>
/// Asserts that a collection of objects contains an 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> ContainEquivalentTo<TExpectation>(TExpectation expectation, Func<EquivalencyAssertionOptions<TExpectation>,
EquivalencyAssertionOptions<TExpectation>> config, string because = "", params object[] becauseArgs)
{
if(ReferenceEquals(Subject, null))
dennisdoomen marked this conversation as resolved.
Show resolved Hide resolved
{
Execute.Assertion
.BecauseOf(because, becauseArgs)
.FailWith("Expected {context:collection} to contain equivalent to {0}{reason}, but found <null>.", expectation);
matthiaslischka marked this conversation as resolved.
Show resolved Hide resolved
}

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

using (var scope = new AssertionScope())
{
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 collection {0} to contain equivalent to {1}.", Subject, expectation);
matthiaslischka marked this conversation as resolved.
Show resolved Hide resolved

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
231 changes: 230 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,236 @@ public void When_testing_collections_not_to_be_equivalent_against_same_collectio

#endregion

#region Contain Equivalent To

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

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

[Fact]
public void When_collection_contains_object_equivalent_to_another_it_should_succeed()
matthiaslischka marked this conversation as resolved.
Show resolved Hide resolved
{
//-----------------------------------------------------------------------------------------------------------
// Arrange
//-----------------------------------------------------------------------------------------------------------
IEnumerable collection = new[] { 1, 2, 3 };
int item = 2;

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

[Fact]
public void When_character_collection_does_contain_equivalent_it_should_succeed()
{
//-----------------------------------------------------------------------------------------------------------
// Arrange
//-----------------------------------------------------------------------------------------------------------
char[] list = ("abc123ab").ToCharArray();
char item = 'c';
matthiaslischka marked this conversation as resolved.
Show resolved Hide resolved

//-----------------------------------------------------------------------------------------------------------
// Act / Assert
//-----------------------------------------------------------------------------------------------------------
list.Should().ContainEquivalentTo(item);
}

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

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

//-----------------------------------------------------------------------------------------------------------
// Act / Assert
//-----------------------------------------------------------------------------------------------------------
act.Should().Throw<XunitException>().WithMessage("Expected collection {1, 2, 3} to contain equivalent to 4.");
matthiaslischka marked this conversation as resolved.
Show resolved Hide resolved
}

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

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

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

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

//-----------------------------------------------------------------------------------------------------------
// Act
//-----------------------------------------------------------------------------------------------------------
Action act = () => subject.Should().ContainEquivalentTo(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().ContainEquivalentTo(item);

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

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

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

//-----------------------------------------------------------------------------------------------------------
// Assert
//-----------------------------------------------------------------------------------------------------------
act.Should().Throw<XunitException>().WithMessage("Expected collection {empty} to contain equivalent to 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().ContainEquivalentTo(item);

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

[Fact]
public void When_collection_does_contain_equivalent_by_including_single_property_it_should_not_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 / Assert
//-----------------------------------------------------------------------------------------------------------
subject.Should().ContainEquivalentTo(item, options => options.Including(x => x.Name));
}

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

//-----------------------------------------------------------------------------------------------------------
// Act / Assert
//-----------------------------------------------------------------------------------------------------------
2.Should().Equals(boxedValue);
matthiaslischka marked this conversation as resolved.
Show resolved Hide resolved
collection.Should().ContainEquivalentTo(boxedValue);
}

#endregion

#region Be Subset Of

[Fact]
Expand Down
3 changes: 3 additions & 0 deletions docs/_pages/documentation.md
Expand Up @@ -461,6 +461,9 @@ collection.Should().NotContain(new[] { 82, 83 });
collection.Should().NotContainNulls();
collection.Should().NotContain(x => x > 10);

object boxedValue = 2;
collection.Should().ContainEquivalentTo(boxedValue); // Compared by object equivalence
const int successor = 5;
const int predecessor = 5;
collection.Should().HaveElementPreceding(successor, element);
Expand Down