Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions Src/FluentAssertions/Collections/CollectionAssertions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,98 @@ public AndConstraint<TAssertions> NotBeEquivalentTo(IEnumerable unexpected, stri
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);
Comment thread
matthiaslischka marked this conversation as 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
Original file line number Diff line number Diff line change
@@ -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';
Comment thread
matthiaslischka marked this conversation as 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
Loading