Collections
A collection object in .NET is so versatile that the number of assertions on them require the same level of versatility. Most, if not all, are so self-explanatory that we’ll just list them here.
IEnumerable<int> collection = new[] { 1, 2, 5, 8 };
collection.Should().NotBeEmpty()
.And.HaveCount(4)
.And.ContainInOrder(new[] { 2, 5 })
.And.ContainItemsAssignableTo<int>();
collection.Should().Equal(new List<int> { 1, 2, 5, 8 });
collection.Should().Equal(1, 2, 5, 8);
collection.Should().NotEqual(8, 2, 3, 5);
collection.Should().BeEquivalentTo(new[] {8, 2, 1, 5});
collection.Should().NotBeEquivalentTo(new[] {8, 2, 3, 5});
collection.Should().HaveCount(c => c > 3)
.And.OnlyHaveUniqueItems();
collection.Should().HaveCountGreaterThan(3);
collection.Should().HaveCountGreaterThanOrEqualTo(4);
collection.Should().HaveCountLessThanOrEqualTo(4);
collection.Should().HaveCountLessThan(5);
collection.Should().NotHaveCount(3);
collection.Should().HaveSameCount(new[] { 6, 2, 0, 5 });
collection.Should().NotHaveSameCount(new[] { 6, 2, 0 });
collection.Should().StartWith(1);
collection.Should().StartWith(new[] { 1, 2 });
collection.Should().EndWith(8);
collection.Should().EndWith(new[] { 5, 8 });
collection.Should().BeSubsetOf(new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, });
collection.Should().ContainSingle();
collection.Should().ContainSingle(x => x > 3);
collection.Should().Contain(8)
.And.HaveElementAt(2, 5)
.And.NotBeSubsetOf(new[] {11, 56});
collection.Should().Contain(x => x > 3);
collection.Should().Contain(collection, "", 5, 6); // It should contain the original items, plus 5 and 6.
collection.Should().OnlyContain(x => x < 10);
collection.Should().ContainItemsAssignableTo<int>();
collection.Should().NotContainItemsAssignableTo<string>()
collection.Should().ContainInOrder(new[] { 1, 5, 8 });
collection.Should().NotContainInOrder(new[] { 5, 1, 2 });
collection.Should().ContainInConsecutiveOrder(new[] { 2, 5, 8 });
collection.Should().NotContainInConsecutiveOrder(new[] { 1, 5, 8});
collection.Should().NotContain(82);
collection.Should().NotContain(new[] { 82, 83 });
collection.Should().NotContainNulls();
collection.Should().NotContain(x => x > 10);
object boxedValue = 2;
collection.Should().ContainEquivalentOf(boxedValue); // Compared by object equivalence
object unexpectedBoxedValue = 82;
collection.Should().NotContainEquivalentOf(unexpectedBoxedValue); // Compared by object equivalence
const int successor = 5;
const int predecessor = 5;
collection.Should().HaveElementPreceding(successor, element);
collection.Should().HaveElementSucceeding(predecessor, element);
collection.Should().BeEmpty();
collection.Should().BeNullOrEmpty();
collection.Should().NotBeNullOrEmpty();
IEnumerable<int> otherCollection = new[] { 1, 2, 5, 8, 1 };
IEnumerable<int> anotherCollection = new[] { 10, 20, 50, 80, 10 };
collection.Should().IntersectWith(otherCollection);
collection.Should().NotIntersectWith(anotherCollection);
var singleEquivalent = new[] { new { Size = 42 } };
singleEquivalent.Should().ContainSingle()
.Which.Should().BeEquivalentTo(new { Size = 42 });
Asserting that a collection contains items in a certain order is as easy as using one of the several overloads of BeInAscendingOrder
or BeInDescendingOrder
. The default overload will use the default Comparer
for the specified type, but overloads also exist that take an IComparer<T>
, a property expression to sort by an object’s property, or a lambda expression to avoid the need for IComparer<T>
implementations.
collection.Should().BeInAscendingOrder();
collection.Should().BeInDescendingOrder();
collection.Should().NotBeInAscendingOrder();
collection.Should().NotBeInDescendingOrder();
Since 6.9.0 there is a slight change in how culture is taken into comparison. Now by default StringComparer.Ordinal
is used to compare strings.
If you want to overrule this behavior use the IComparer<T>
overload. The use case is e.g. if you get data from a database ordered by culture aware sort order.
collection.Should().BeInAscendingOrder(item => item.SomeProp, StringComparer.CurrentCulture);
collection.Should().BeInDescendingOrder(item => item.SomeProp, StringComparer.CurrentCulture);
collection.Should().NotBeInAscendingOrder(item => item.SomeProp, StringComparer.CurrentCulture);
collection.Should().NotBeInDescendingOrder(item => item.SomeProp, StringComparer.CurrentCulture);
For String
collections there are specific methods to assert the items. For the ContainMatch
and NotContainMatch
methods we support wildcards.
The pattern can be a combination of literal and wildcard characters, but it doesn’t support regular expressions.
The following wildcard specifiers are permitted in the pattern:
Wildcard specifier | Matches |
---|---|
* (asterisk) | Zero or more characters in that position. |
? (question mark) | Exactly one character in that position. |
IEnumerable<string> stringCollection = new[] { "build succeeded", "test failed" };
stringCollection.Should().AllBe("build succeeded");
stringCollection.Should().ContainMatch("* failed");
In order to assert presence of an equivalent item in a collection applying Object graph comparison rules, use this:
collection.Should().ContainEquivalentOf(boxedValue);
collection.Should().NotContainEquivalentOf(unexpectedBoxedValue)
Those last two methods can be used to assert a collection contains items in ascending or descending order.
For simple types that might be fine, but for more complex types, it requires you to implement IComparable
, something that doesn’t make a whole lot of sense in all cases.
That’s why we offer overloads that take an expression.
collection.Should().BeInAscendingOrder(x => x.SomeProperty);
collection.Should().BeInDescendingOrder(x => x.SomeProperty);
collection.Should().NotBeInAscendingOrder(x => x.SomeProperty);
collection.Should().NotBeInDescendingOrder(x => x.SomeProperty);
When asserting on a projection of a collection the failure message will be less descriptive as it only knows about the projected value and not object containing that property.
collection.Select(x => x.SomeProperty).Should().OnlyHaveUniqueItems();
Therefore we offer two overloads that takes an expression to select the property.
collection.Should().OnlyHaveUniqueItems(x => x.SomeProperty);
collection.Should().NotContainNulls(x => x.SomeProperty);
Special overloads of Equal()
, StartWith
and EndWith
take a lambda that is used for checking the two collections without relying on the type’s Equals() method.
Consider for instance two collections that contain some kind of domain entity persisted to a database and then reloaded.
Since the actual object instance is different, if you want to make sure a particular property was properly persisted, you usually do something like this:
persistedCustomers.Select(c => c.Name).Should().Equal(customers.Select(c => c.Name));
persistedCustomers.Select(c => c.Name).Should().StartWith(customers.Select(c => c.Name));
persistedCustomers.Select(c => c.Name).Should().EndWith(customers.Select(c => c.Name));
With these new overloads, you can rewrite them into:
persistedCustomers.Should().Equal(customers, (c1, c2) => c1.Name == c2.Name);
persistedCustomers.Should().StartWith(customers, (c1, c2) => c1.Name == c2.Name);
persistedCustomers.Should().EndWith(customers, (c1, c2) => c1.Name == c2.Name);
You can also perform assertions on all elements of a collection:
IEnumerable<BaseType> collection = new BaseType[] { new DerivedType() };
collection.Should().AllBeAssignableTo<DerivedType>();
collection.Should().AllBeOfType<DerivedType>();
collection.Should().AllBeEquivalentTo(referenceObject);
In case if you need to perform individual assertions on all elements of a collection, you can assert each element separately in the following manner:
var collection = new []
{
new { Id = 1, Name = "John", Attributes = new string[] { } },
new { Id = 2, Name = "Jane", Attributes = new string[] { "attr" } }
};
collection.Should().SatisfyRespectively(
first =>
{
first.Id.Should().Be(1);
first.Name.Should().StartWith("J");
first.Attributes.Should().NotBeNull();
},
second =>
{
second.Id.Should().Be(2);
second.Name.Should().EndWith("e");
second.Attributes.Should().NotBeEmpty();
});
If you need to perform the same assertion on all elements of a collection:
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:
var collection = new []
{
new { Id = 1, Name = "John", Attributes = new string[] { } },
new { Id = 2, Name = "Jane", Attributes = new string[] { "attr" } }
};
collection.Should().Satisfy(
e => e.Id == 2 && e.Name == "Jane" && e.Attributes == null,
e => e.Id == 1 && e.Name == "John" && e.Attributes != null && e.Attributes.Length > 0);