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

Added SatisfyAll #1790

Merged
merged 9 commits into from Jan 30, 2022
60 changes: 60 additions & 0 deletions Src/FluentAssertions/Collections/GenericCollectionAssertions.cs
Expand Up @@ -2671,6 +2671,66 @@ public AndConstraint<TAssertions> OnlyHaveUniqueItems(string because = "", param
return new AndConstraint<TAssertions>((TAssertions)this);
}

/// <summary>
/// Asserts that a collection contains only items which meet
/// the criteria provided by the inspector.
/// </summary>
/// <param name="expected">
/// The element inspector, which inspects each element in turn.
/// </param>
/// <param name="because">
/// A 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 <paramref name="because"/>.
/// </param>
/// <exception cref="ArgumentNullException"><paramref name="expected"/> is <c>null</c>.</exception>
public AndConstraint<TAssertions> AllSatisfy(Action<T> expected, string because = "", params object[] becauseArgs)
{
Guard.ThrowIfArgumentIsNull(expected, nameof(expected), "Cannot verify against a <null> inspector");

bool success = Execute.Assertion
.BecauseOf(because, becauseArgs)
.WithExpectation("Expected {context:collection} to contain only items satisfying the inspector{reason}, ")
.Given(() => Subject)
.ForCondition(subject => subject is not null)
.FailWith("but collection is <null>.")
.Then
.ForCondition(subject => subject.Any())
.FailWith("but collection is empty.")
.Then
.ClearExpectation();

if (success)
{
string[] failuresFromInspectors;

using (CallerIdentifier.OverrideStackSearchUsingCurrentScope())
{
var elementInspectors = Subject.Select(_ => expected);
failuresFromInspectors = CollectFailuresFromInspectors(elementInspectors);
}

if (failuresFromInspectors.Any())
{
string failureMessage = Environment.NewLine
+ string.Join(Environment.NewLine, failuresFromInspectors.Select(x => x.IndentLines()));

Execute.Assertion
.BecauseOf(because, becauseArgs)
.WithExpectation("Expected {context:collection} to contain only items satisfying the inspector{reason}:")
.FailWithPreFormatted(failureMessage)
.Then
.ClearExpectation();
}

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

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

/// <summary>
/// Asserts that a collection contains exactly a given number of elements, which meet
/// the criteria provided by the element inspectors.
Expand Down
Expand Up @@ -392,6 +392,7 @@ namespace FluentAssertions.Collections
public FluentAssertions.AndConstraint<TAssertions> AllBeEquivalentTo<TExpectation>(TExpectation expectation, System.Func<FluentAssertions.Equivalency.EquivalencyAssertionOptions<TExpectation>, FluentAssertions.Equivalency.EquivalencyAssertionOptions<TExpectation>> config, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> AllBeOfType(System.Type expectedType, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndWhichConstraint<TAssertions, System.Collections.Generic.IEnumerable<TExpectation>> AllBeOfType<TExpectation>(string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> AllSatisfy(System.Action<T> expected, string because = "", params object[] becauseArgs) { }
protected void AssertCollectionEndsWith<TActual, TExpectation>(System.Collections.Generic.IEnumerable<TActual> actual, System.Collections.Generic.ICollection<TExpectation> expected, System.Func<TActual, TExpectation, bool> equalityComparison, string because = "", params object[] becauseArgs) { }
protected void AssertCollectionStartsWith<TActual, TExpectation>(System.Collections.Generic.IEnumerable<TActual> actualItems, System.Collections.Generic.ICollection<TExpectation> expected, System.Func<TActual, TExpectation, bool> equalityComparison, string because = "", params object[] becauseArgs) { }
protected void AssertSubjectEquality<TExpectation>(System.Collections.Generic.IEnumerable<TExpectation> expectation, System.Func<T, TExpectation, bool> equalityComparison, string because = "", params object[] becauseArgs) { }
Expand Down
Expand Up @@ -392,6 +392,7 @@ namespace FluentAssertions.Collections
public FluentAssertions.AndConstraint<TAssertions> AllBeEquivalentTo<TExpectation>(TExpectation expectation, System.Func<FluentAssertions.Equivalency.EquivalencyAssertionOptions<TExpectation>, FluentAssertions.Equivalency.EquivalencyAssertionOptions<TExpectation>> config, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> AllBeOfType(System.Type expectedType, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndWhichConstraint<TAssertions, System.Collections.Generic.IEnumerable<TExpectation>> AllBeOfType<TExpectation>(string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> AllSatisfy(System.Action<T> expected, string because = "", params object[] becauseArgs) { }
protected void AssertCollectionEndsWith<TActual, TExpectation>(System.Collections.Generic.IEnumerable<TActual> actual, System.Collections.Generic.ICollection<TExpectation> expected, System.Func<TActual, TExpectation, bool> equalityComparison, string because = "", params object[] becauseArgs) { }
protected void AssertCollectionStartsWith<TActual, TExpectation>(System.Collections.Generic.IEnumerable<TActual> actualItems, System.Collections.Generic.ICollection<TExpectation> expected, System.Func<TActual, TExpectation, bool> equalityComparison, string because = "", params object[] becauseArgs) { }
protected void AssertSubjectEquality<TExpectation>(System.Collections.Generic.IEnumerable<TExpectation> expectation, System.Func<T, TExpectation, bool> equalityComparison, string because = "", params object[] becauseArgs) { }
Expand Down
Expand Up @@ -392,6 +392,7 @@ namespace FluentAssertions.Collections
public FluentAssertions.AndConstraint<TAssertions> AllBeEquivalentTo<TExpectation>(TExpectation expectation, System.Func<FluentAssertions.Equivalency.EquivalencyAssertionOptions<TExpectation>, FluentAssertions.Equivalency.EquivalencyAssertionOptions<TExpectation>> config, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> AllBeOfType(System.Type expectedType, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndWhichConstraint<TAssertions, System.Collections.Generic.IEnumerable<TExpectation>> AllBeOfType<TExpectation>(string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> AllSatisfy(System.Action<T> expected, string because = "", params object[] becauseArgs) { }
protected void AssertCollectionEndsWith<TActual, TExpectation>(System.Collections.Generic.IEnumerable<TActual> actual, System.Collections.Generic.ICollection<TExpectation> expected, System.Func<TActual, TExpectation, bool> equalityComparison, string because = "", params object[] becauseArgs) { }
protected void AssertCollectionStartsWith<TActual, TExpectation>(System.Collections.Generic.IEnumerable<TActual> actualItems, System.Collections.Generic.ICollection<TExpectation> expected, System.Func<TActual, TExpectation, bool> equalityComparison, string because = "", params object[] becauseArgs) { }
protected void AssertSubjectEquality<TExpectation>(System.Collections.Generic.IEnumerable<TExpectation> expectation, System.Func<T, TExpectation, bool> equalityComparison, string because = "", params object[] becauseArgs) { }
Expand Down
Expand Up @@ -385,6 +385,7 @@ namespace FluentAssertions.Collections
public FluentAssertions.AndConstraint<TAssertions> AllBeEquivalentTo<TExpectation>(TExpectation expectation, System.Func<FluentAssertions.Equivalency.EquivalencyAssertionOptions<TExpectation>, FluentAssertions.Equivalency.EquivalencyAssertionOptions<TExpectation>> config, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> AllBeOfType(System.Type expectedType, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndWhichConstraint<TAssertions, System.Collections.Generic.IEnumerable<TExpectation>> AllBeOfType<TExpectation>(string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> AllSatisfy(System.Action<T> expected, string because = "", params object[] becauseArgs) { }
protected void AssertCollectionEndsWith<TActual, TExpectation>(System.Collections.Generic.IEnumerable<TActual> actual, System.Collections.Generic.ICollection<TExpectation> expected, System.Func<TActual, TExpectation, bool> equalityComparison, string because = "", params object[] becauseArgs) { }
protected void AssertCollectionStartsWith<TActual, TExpectation>(System.Collections.Generic.IEnumerable<TActual> actualItems, System.Collections.Generic.ICollection<TExpectation> expected, System.Func<TActual, TExpectation, bool> equalityComparison, string because = "", params object[] becauseArgs) { }
protected void AssertSubjectEquality<TExpectation>(System.Collections.Generic.IEnumerable<TExpectation> expectation, System.Func<T, TExpectation, bool> equalityComparison, string because = "", params object[] becauseArgs) { }
Expand Down
Expand Up @@ -392,6 +392,7 @@ namespace FluentAssertions.Collections
public FluentAssertions.AndConstraint<TAssertions> AllBeEquivalentTo<TExpectation>(TExpectation expectation, System.Func<FluentAssertions.Equivalency.EquivalencyAssertionOptions<TExpectation>, FluentAssertions.Equivalency.EquivalencyAssertionOptions<TExpectation>> config, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> AllBeOfType(System.Type expectedType, string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndWhichConstraint<TAssertions, System.Collections.Generic.IEnumerable<TExpectation>> AllBeOfType<TExpectation>(string because = "", params object[] becauseArgs) { }
public FluentAssertions.AndConstraint<TAssertions> AllSatisfy(System.Action<T> expected, string because = "", params object[] becauseArgs) { }
protected void AssertCollectionEndsWith<TActual, TExpectation>(System.Collections.Generic.IEnumerable<TActual> actual, System.Collections.Generic.ICollection<TExpectation> expected, System.Func<TActual, TExpectation, bool> equalityComparison, string because = "", params object[] becauseArgs) { }
protected void AssertCollectionStartsWith<TActual, TExpectation>(System.Collections.Generic.IEnumerable<TActual> actualItems, System.Collections.Generic.ICollection<TExpectation> expected, System.Func<TActual, TExpectation, bool> equalityComparison, string because = "", params object[] becauseArgs) { }
protected void AssertSubjectEquality<TExpectation>(System.Collections.Generic.IEnumerable<TExpectation> expectation, System.Func<T, TExpectation, bool> equalityComparison, string because = "", params object[] becauseArgs) { }
Expand Down
@@ -0,0 +1,133 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FluentAssertions.Execution;
using Xunit;
using Xunit.Sdk;

namespace FluentAssertions.Specs.Collections
{
/// <content>
/// The AllSatisfy specs.
/// </content>
public partial class CollectionAssertionSpecs
dennisdoomen marked this conversation as resolved.
Show resolved Hide resolved
{
public class AllSatisfy
{
[Fact]
public void A_null_inspector_should_throw()
{
// Arrange
IEnumerable<int> collection = new[] { 1, 2 };

// Act
Action act = () => collection.Should().AllSatisfy(null);

// Assert
act.Should()
.Throw<ArgumentException>()
.WithMessage("Cannot verify against a <null> inspector*");
}

[Fact]
public void A_null_collection_should_throw()
{
// Arrange
IEnumerable<int> collection = null;

// Act
Action act = () =>
{
using var _ = new AssertionScope();
collection.Should().AllSatisfy(x => x.Should().Be(1), "because we want to test the failure {0}", "message");
};

// Assert
act.Should()
.Throw<XunitException>()
.WithMessage(
"Expected collection to contain only items satisfying the inspector because we want to test the failure message, but collection is <null>.");
}

[Fact]
public void An_empty_collection_should_throw()
{
// Arrange
var collection = Enumerable.Empty<int>();

// Act
Action act = ()
=> collection.Should()
.AllSatisfy(x => x.Should().Be(1), "because we want to test the failure {0}", "message");

// Assert
act.Should()
.Throw<XunitException>()
.WithMessage(
"Expected collection to contain only items satisfying the inspector because we want to test the failure message, but collection is empty.");
}

[Fact]
public void All_items_satisfying_inspector_should_succeed()
{
// Arrange
var collection = new[] { new Customer { Age = 21, Name = "John" }, new Customer { Age = 21, Name = "Jane" } };

// Act / Assert
collection.Should().AllSatisfy(x => x.Age.Should().Be(21));
}

[Fact]
public void Any_items_not_satisfying_inspector_should_throw()
{
// Arrange
var customers = new[]
{
new CustomerWithItems { Age = 21, Items = new[] { 1, 2 } },
new CustomerWithItems { Age = 22, Items = new[] { 3 } }
};

// Act
Action act = () => customers.Should()
.AllSatisfy(
customer =>
{
customer.Age.Should().BeLessThan(21);
customer.Items.Should()
.AllSatisfy(item => item.Should().Be(3));
},
"because we want to test {0}",
"nested assertions");

// Assert
act.Should()
.Throw<XunitException>()
.WithMessage(
@"Expected customers to contain only items satisfying the inspector because we want to test nested assertions:
*At index 0:
*Expected customer.Age to be less than 21, but found 21
*Expected customer.Items to contain only items satisfying the inspector:
*At index 0:
*Expected item to be 3, but found 1
*At index 1:
*Expected item to be 3, but found 2
*At index 1:
*Expected customer.Age to be less than 21, but found 22"
);
}

[Fact]
public void Inspector_message_that_is_not_reformatable_should_not_throw()
{
// Arrange
byte[][] subject = { new byte[] { 1 } };

// Act
Action act = () => subject.Should().AllSatisfy(e => e.Should().BeEquivalentTo(new byte[] { 2, 3, 4 }));

// Assert
act.Should().NotThrow<FormatException>();
}
}
}
}
@@ -0,0 +1,44 @@
using System;
using Xunit;
using Xunit.Sdk;

namespace FluentAssertions.Specs.Collections
{
public partial class GenericCollectionAssertionOfStringSpecs
{
public class AllSatisfy
{
[Fact]
public void All_items_satisfying_inspector_should_succeed()
{
// Arrange
string[] collection = new[] { "John", "John" };

// Act / Assert
collection.Should().AllSatisfy(value => value.Should().Be("John"));
}

[Fact]
public void Any_items_not_satisfying_inspector_should_throw()
{
// Arrange
string[] collection = new[] { "Jack", "Jessica" };

// Act
Action act = () => collection.Should()
.AllSatisfy(
value => value.Should().Be("John"),
"because we want to test the failure {0}",
"message");

// Assert
act.Should()
.Throw<XunitException>()
.WithMessage(
"Expected collection to contain only items satisfying the inspector because we want to test the failure message:"
+ "*John*Jack"
+ "*John*Jessica*");
}
}
}
}
Expand Up @@ -9,7 +9,7 @@

namespace FluentAssertions.Specs.Collections
{
public class GenericCollectionAssertionOfStringSpecs
public partial class GenericCollectionAssertionOfStringSpecs
{
[Fact]
public void Should_fail_when_asserting_collection_has_a_count_that_is_different_from_the_number_of_items()
Expand Down
16 changes: 16 additions & 0 deletions docs/_pages/collections.md
Expand Up @@ -190,6 +190,22 @@ collection.Should().SatisfyRespectively(
});
```

If you need to perform the same assertion on all elements of a collection:

```csharp
var collection = new []
{
new { Id = 1, Name = "John", Attributes = new string[] { } },
new { Id = 2, Name = "Jane", Attributes = new string[] { "attr" } }
};
collection.Should().AllSatisfy(x =>
{
x.Id.Should().BePositive();
x.Name.Should().StartWith("J");
x.Attributes.Should().NotBeNull();
});
```

If you need to perform individual assertions on all elements of a collection without setting expectation about the order of elements:

```csharp
Expand Down
1 change: 1 addition & 0 deletions docs/_pages/releases.md
Expand Up @@ -11,6 +11,7 @@ sidebar:

### What's New

* Added `AllSatisfy` for asserting all items in a collection satisfy an inspector - [#1790](https://github.com/fluentassertions/fluentassertions/pull/1790)
* Added `WithMapping` option to `BeEquivalentTo` to map members with different names between the subject and expectation - [#1742](https://github.com/fluentassertions/fluentassertions/pull/1742)

### Fixes (Extensibility)
Expand Down