Collection Assertions
TUnit provides comprehensive assertions for testing collections, including membership, count, ordering, and equivalency checks. These assertions work with any IEnumerable<T>.
Membership Assertionsâ
Contains (Item)â
Tests that a collection contains a specific item:
[Test]
public async Task Collection_Contains_Item()
{
var numbers = new[] { 1, 2, 3, 4, 5 };
await Assert.That(numbers).Contains(3);
await Assert.That(numbers).Contains(1);
}
Works with any collection type:
[Test]
public async Task Various_Collection_Types()
{
var list = new List<string> { "apple", "banana", "cherry" };
await Assert.That(list).Contains("banana");
var hashSet = new HashSet<int> { 10, 20, 30 };
await Assert.That(hashSet).Contains(20);
var queue = new Queue<string>(new[] { "first", "second" });
await Assert.That(queue).Contains("first");
}
Contains (Predicate)â
Tests that a collection contains an item matching a predicate, and returns that item:
[Test]
public async Task Collection_Contains_Matching_Item()
{
var users = new[]
{
new User { Name = "Alice", Age = 30 },
new User { Name = "Bob", Age = 25 }
};
// Returns the found item
var user = await Assert.That(users).Contains(u => u.Name == "Alice");
// Can assert on the returned item
await Assert.That(user.Age).IsEqualTo(30);
}
DoesNotContain (Item)â
Tests that a collection does not contain a specific item:
[Test]
public async Task Collection_Does_Not_Contain()
{
var numbers = new[] { 1, 2, 3, 4, 5 };
await Assert.That(numbers).DoesNotContain(10);
await Assert.That(numbers).DoesNotContain(0);
}
DoesNotContain (Predicate)â
Tests that no items match the predicate:
[Test]
public async Task Collection_Does_Not_Contain_Matching()
{
var users = new[]
{
new User { Name = "Alice", Age = 30 },
new User { Name = "Bob", Age = 25 }
};
await Assert.That(users).DoesNotContain(u => u.Age > 50);
await Assert.That(users).DoesNotContain(u => u.Name == "Charlie");
}
Count Assertionsâ
Countâ
Tests that a collection has an exact count:
[Test]
public async Task Collection_Has_Count()
{
var numbers = new[] { 1, 2, 3, 4, 5 };
await Assert.That(numbers).Count().IsEqualTo(5);
}
Count with Comparisonâ
Get the count for further assertions:
[Test]
public async Task Count_With_Comparison()
{
var numbers = new[] { 1, 2, 3, 4, 5 };
await Assert.That(numbers)
.Count().IsEqualTo(5);
await Assert.That(numbers)
.Count().IsGreaterThan(3)
.And.Count().IsLessThan(10);
}
Count with Inner Assertionâ
Count items that satisfy an assertion, allowing you to reuse existing assertion methods:
[Test]
public async Task Count_With_Inner_Assertion()
{
var numbers = new[] { 1, 2, 3, 4, 5, 6 };
// Count numbers greater than 3 using assertion builder
await Assert.That(numbers)
.Count(item => item.IsGreaterThan(3))
.IsEqualTo(3);
// Count numbers between 2 and 5
await Assert.That(numbers)
.Count(item => item.IsBetween(2, 5))
.IsEqualTo(4);
}
[Test]
public async Task Count_Strings_With_Inner_Assertion()
{
var names = new[] { "Alice", "Bob", "Andrew", "Charlie" };
// Count names starting with "A"
await Assert.That(names)
.Count(item => item.StartsWith("A"))
.IsEqualTo(2);
}
Count with Collection Chainingâ
Count assertions preserve the collection type, allowing you to chain additional collection assertions:
[Test]
public async Task Count_With_Chaining()
{
var numbers = new[] { 1, 2, 3, 4, 5 };
// Assert count and then chain with other collection assertions
await Assert.That(numbers)
.Count().IsEqualTo(5)
.And.Contains(3)
.And.IsInOrder();
// Count with item filter assertion, then chain
await Assert.That(numbers)
.Count(item => item.IsGreaterThan(2)).IsEqualTo(3)
.And.Contains(5)
.And.All(x => x > 0);
// For non-int collections, you can also use inline count assertions
var names = new[] { "Alice", "Bob", "Charlie" };
await Assert.That(names)
.Count(c => c.IsEqualTo(3))
.And.Contains("Bob");
}
IsEmptyâ
Tests that a collection has no items:
[Test]
public async Task Collection_Is_Empty()
{
var empty = new List<int>();
await Assert.That(empty).IsEmpty();
await Assert.That(empty).Count().IsEqualTo(0);
}
IsNotEmptyâ
Tests that a collection has at least one item:
[Test]
public async Task Collection_Is_Not_Empty()
{
var numbers = new[] { 1 };
await Assert.That(numbers).IsNotEmpty();
}
HasSingleItemâ
Tests that a collection has exactly one item, and returns that item:
[Test]
public async Task Collection_Has_Single_Item()
{
var users = new[] { new User { Name = "Alice", Age = 30 } };
var user = await Assert.That(users).HasSingleItem();
await Assert.That(user.Name).IsEqualTo("Alice");
}
Ordering Assertionsâ
IsInOrderâ
Tests that a collection is sorted in ascending order:
[Test]
public async Task Collection_In_Ascending_Order()
{
var numbers = new[] { 1, 2, 3, 4, 5 };
await Assert.That(numbers).IsInOrder();
}
[Test]
public async Task Strings_In_Order()
{
var names = new[] { "Alice", "Bob", "Charlie" };
await Assert.That(names).IsInOrder();
}
IsInDescendingOrderâ
Tests that a collection is sorted in descending order:
[Test]
public async Task Collection_In_Descending_Order()
{
var numbers = new[] { 5, 4, 3, 2, 1 };
await Assert.That(numbers).IsInDescendingOrder();
}
IsOrderedByâ
Tests that a collection is ordered by a specific property:
[Test]
public async Task Ordered_By_Property()
{
var users = new[]
{
new User { Name = "Alice", Age = 25 },
new User { Name = "Bob", Age = 30 },
new User { Name = "Charlie", Age = 35 }
};
await Assert.That(users).IsOrderedBy(u => u.Age);
}
IsOrderedByDescendingâ
Tests that a collection is ordered by a property in descending order:
[Test]
public async Task Ordered_By_Descending()
{
var users = new[]
{
new User { Name = "Charlie", Age = 35 },
new User { Name = "Bob", Age = 30 },
new User { Name = "Alice", Age = 25 }
};
await Assert.That(users).IsOrderedByDescending(u => u.Age);
}
Predicate-Based Assertionsâ
Allâ
Tests that all items satisfy a condition:
[Test]
public async Task All_Items_Match()
{
var numbers = new[] { 2, 4, 6, 8 };
await Assert.That(numbers).All(n => n % 2 == 0);
}
With Satisfyâ
The single parameter overload will match T from IEnumerable<T> - Giving you the relevant assertions for that type.
[Test]
public async Task All_Satisfy_With_Property()
{
var users = new[]
{
new User { Name = "Alice", Age = 25 },
new User { Name = "Bob", Age = 30 }
};
// Use the mapper overload to access item properties
await Assert.That(users)
.All()
.Satisfy(user => user.IsNotNull());
}
You can also map to other types by accessing properties an such - And then assert on those specific values:
[Test]
public async Task All_Satisfy_With_Mapper()
{
var users = new[]
{
new User { Name = "Alice", Age = 25 },
new User { Name = "Bob", Age = 30 }
};
await Assert.That(users)
.All()
.Satisfy(
u => u.Age,
age => age.IsGreaterThan(18)
);
}
Anyâ
Tests that at least one item satisfies a condition:
[Test]
public async Task Any_Item_Matches()
{
var numbers = new[] { 1, 3, 5, 6, 7 };
await Assert.That(numbers).Any(n => n % 2 == 0);
}
Equivalency Assertionsâ
Collection equivalency checks whether two collections contain the same elements. By default, order is ignored - only the presence and count of elements matter. To require matching order, use the CollectionOrdering.Matching parameter.
IsEquivalentToâ
Tests that two collections contain the same items. By default, order is ignored (use CollectionOrdering.Matching to require matching order):
[Test]
public async Task Collections_Are_Equivalent()
{
var actual = new[] { 1, 2, 3, 4, 5 };
var expected = new[] { 5, 4, 3, 2, 1 };
await Assert.That(actual).IsEquivalentTo(expected);
}
Different collection types:
[Test]
public async Task Different_Collection_Types()
{
var list = new List<int> { 1, 2, 3 };
var array = new[] { 3, 2, 1 };
await Assert.That(list).IsEquivalentTo(array);
}
With Custom Comparerâ
[Test]
public async Task Equivalent_With_Comparer()
{
var actual = new[] { "apple", "banana", "cherry" };
var expected = new[] { "APPLE", "BANANA", "CHERRY" };
await Assert.That(actual)
.IsEquivalentTo(expected)
.Using(StringComparer.OrdinalIgnoreCase);
}
With Custom Equality Predicateâ
[Test]
public async Task Equivalent_With_Predicate()
{
var users1 = new[]
{
new User { Name = "Alice", Age = 30 },
new User { Name = "Bob", Age = 25 }
};
var users2 = new[]
{
new User { Name = "Bob", Age = 25 },
new User { Name = "Alice", Age = 30 }
};
await Assert.That(users1)
.IsEquivalentTo(users2)
.Using((u1, u2) => u1.Name == u2.Name && u1.Age == u2.Age);
}
Order-Independent Comparison (Default)â
By default, IsEquivalentTo ignores the order of elements:
[Test]
public async Task Equivalent_Ignoring_Order()
{
var actual = new[] { 1, 2, 3 };
var expected = new[] { 3, 2, 1 };
// Order is ignored by default
await Assert.That(actual).IsEquivalentTo(expected);
}
Requiring Matching Orderâ
To require elements to be in the same order, pass CollectionOrdering.Matching:
[Test]
public async Task Equivalent_With_Matching_Order()
{
var actual = new[] { 1, 2, 3 };
var expected = new[] { 1, 2, 3 };
await Assert.That(actual).IsEquivalentTo(expected, CollectionOrdering.Matching);
}
This will fail if elements are in different positions:
[Test]
public async Task Not_Equivalent_Different_Order()
{
var actual = new[] { 1, 2, 3 };
var expected = new[] { 3, 2, 1 };
// This will fail when requiring matching order
// await Assert.That(actual).IsEquivalentTo(expected, CollectionOrdering.Matching);
}
IsNotEquivalentToâ
Tests that collections are not equivalent:
[Test]
public async Task Collections_Not_Equivalent()
{
var actual = new[] { 1, 2, 3 };
var different = new[] { 4, 5, 6 };
await Assert.That(actual).IsNotEquivalentTo(different);
}
Practical Tips for Collection Orderingâ
When to Use CollectionOrdering.Any (Default)â
The default behavior (ignoring order) is ideal for:
- Testing set operations and results
- Verifying database query results where order isn't guaranteed
- Checking API responses where element order doesn't matter
- Testing collection transformations that may reorder elements
[Test]
public async Task Database_Query_Results()
{
var results = await database.GetActiveUsersAsync();
// Order doesn't matter for this assertion
await Assert.That(results)
.IsEquivalentTo(new[] { user1, user2, user3 });
}
When to Use CollectionOrdering.Matchingâ
Use order-sensitive comparison when:
- Testing sorting algorithms
- Verifying ordered results (e.g., ORDER BY queries)
- Checking sequences where position matters
- Testing priority queues or ordered data structures
[Test]
public async Task Sorted_Query_Results()
{
var results = await database.GetUsersSortedByNameAsync();
// Order matters here
await Assert.That(results)
.IsEquivalentTo(
new[] { alice, bob, charlie },
CollectionOrdering.Matching
);
}
Multiple Assertions with Same Orderingâ
If you need multiple order-sensitive assertions in the same test, consider extracting a helper or being explicit:
[Test]
public async Task Multiple_Order_Sensitive_Checks()
{
var list1 = GetSortedList1();
var list2 = GetSortedList2();
// Be explicit about ordering requirements
await Assert.That(list1).IsEquivalentTo(expected1, CollectionOrdering.Matching);
await Assert.That(list2).IsEquivalentTo(expected2, CollectionOrdering.Matching);
}
For ordered comparisons, you can also use IsInOrder():
[Test]
public async Task Verify_Ordering_Separately()
{
var actual = new[] { 1, 2, 3 };
// Check both equivalency and ordering
await Assert.That(actual).IsEquivalentTo(new[] { 1, 2, 3 });
await Assert.That(actual).IsInOrder();
}
Structural Equivalencyâ
IsStructurallyEqualToâ
Deep comparison of collections including nested objects:
[Test]
public async Task Structurally_Equal()
{
var actual = new[]
{
new { Name = "Alice", Address = new { City = "Seattle" } },
new { Name = "Bob", Address = new { City = "Portland" } }
};
var expected = new[]
{
new { Name = "Alice", Address = new { City = "Seattle" } },
new { Name = "Bob", Address = new { City = "Portland" } }
};
await Assert.That(actual).IsStructurallyEqualTo(expected);
}
IsNotStructurallyEqualToâ
[Test]
public async Task Not_Structurally_Equal()
{
var actual = new[]
{
new { Name = "Alice", Age = 30 }
};
var different = new[]
{
new { Name = "Alice", Age = 31 }
};
await Assert.That(actual).IsNotStructurallyEqualTo(different);
}
Distinctnessâ
HasDistinctItemsâ
Tests that all items in a collection are unique:
[Test]
public async Task All_Items_Distinct()
{
var numbers = new[] { 1, 2, 3, 4, 5 };
await Assert.That(numbers).HasDistinctItems();
}
Fails if duplicates exist:
[Test]
public async Task Duplicates_Fail()
{
var numbers = new[] { 1, 2, 2, 3 };
// This will fail
// await Assert.That(numbers).HasDistinctItems();
}
Practical Examplesâ
Filtering Resultsâ
[Test]
public async Task Filter_And_Assert()
{
var numbers = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var evens = numbers.Where(n => n % 2 == 0).ToArray();
await Assert.That(evens)
.Count().IsEqualTo(5)
.And.All(n => n % 2 == 0);
}
LINQ Query Resultsâ
[Test]
public async Task LINQ_Query_Results()
{
var users = new[]
{
new User { Name = "Alice", Age = 25 },
new User { Name = "Bob", Age = 30 },
new User { Name = "Charlie", Age = 35 }
};
var adults = users.Where(u => u.Age >= 18).ToArray();
await Assert.That(adults)
.Count().IsEqualTo(3)
.And.All(u => u.Age >= 18);
}
Sorting Validationâ
[Test]
public async Task Verify_Sorting()
{
var unsorted = new[] { 5, 2, 8, 1, 9 };
var sorted = unsorted.OrderBy(x => x).ToArray();
await Assert.That(sorted).IsInOrder();
await Assert.That(sorted).IsEquivalentTo(unsorted);
}
API Response Validationâ
[Test]
public async Task API_Returns_Expected_Items()
{
var response = await _api.GetUsersAsync();
await Assert.That(response)
.IsNotEmpty()
.And.All(u => u.Id > 0)
.And.All(u => !string.IsNullOrEmpty(u.Name));
}
Collection Transformationâ
[Test]
public async Task Map_And_Verify()
{
var users = new[]
{
new User { Name = "Alice", Age = 25 },
new User { Name = "Bob", Age = 30 }
};
var names = users.Select(u => u.Name).ToArray();
await Assert.That(names)
.Count().IsEqualTo(2)
.And.Contains("Alice")
.And.Contains("Bob")
.And.All(name => !string.IsNullOrEmpty(name));
}
Empty vs Null Collectionsâ
[Test]
public async Task Empty_vs_Null()
{
List<int>? nullList = null;
List<int> emptyList = new();
List<int> populated = new() { 1, 2, 3 };
await Assert.That(nullList).IsNull();
await Assert.That(emptyList).IsNotNull();
await Assert.That(emptyList).IsEmpty();
await Assert.That(populated).IsNotEmpty();
}
Nested Collectionsâ
[Test]
public async Task Nested_Collections()
{
var matrix = new[]
{
new[] { 1, 2, 3 },
new[] { 4, 5, 6 },
new[] { 7, 8, 9 }
};
await Assert.That(matrix).Count().IsEqualTo(3);
await Assert.That(matrix).All(row => row.Length == 3);
// Flatten and assert
var flattened = matrix.SelectMany(x => x).ToArray();
await Assert.That(flattened).Count().IsEqualTo(9);
}
Collection of Collectionsâ
[Test]
public async Task Collection_Of_Collections()
{
var groups = new List<List<int>>
{
new() { 1, 2 },
new() { 3, 4, 5 },
new() { 6 }
};
await Assert.That(groups)
.Count().IsEqualTo(3)
.And.All(group => group.Count > 0);
}
Chaining Collection Assertionsâ
[Test]
public async Task Chained_Collection_Assertions()
{
var numbers = new[] { 1, 2, 3, 4, 5 };
await Assert.That(numbers)
.IsNotEmpty()
.And.Count().IsEqualTo(5)
.And.Contains(3)
.And.DoesNotContain(10)
.And.IsInOrder()
.And.All(n => n > 0)
.And.Any(n => n == 5);
}
Performance Considerationsâ
Materialize IEnumerableâ
[Test]
public async Task Materialize_Before_Multiple_Assertions()
{
// This query is deferred
IEnumerable<int> query = Enumerable.Range(1, 1000000)
.Where(n => n % 2 == 0);
// Materialize once to avoid re-execution
var materialized = query.ToArray();
await Assert.That(materialized).Count().IsGreaterThan(1000);
await Assert.That(materialized).Contains(100);
await Assert.That(materialized).All(n => n % 2 == 0);
}
Working with HashSet and SortedSetâ
[Test]
public async Task HashSet_Assertions()
{
var set = new HashSet<int> { 1, 2, 3, 4, 5 };
await Assert.That(set)
.Count().IsEqualTo(5)
.And.Contains(3)
.And.HasDistinctItems();
}
[Test]
public async Task SortedSet_Assertions()
{
var sorted = new SortedSet<int> { 5, 2, 8, 1, 9 };
await Assert.That(sorted)
.IsInOrder()
.And.HasDistinctItems();
}
Common Patternsâ
Validate All Itemsâ
[Test]
public async Task Validate_Each_Item()
{
var users = GetUsers();
await using (Assert.Multiple())
{
foreach (var user in users)
{
await Assert.That(user.Name).IsNotEmpty();
await Assert.That(user.Age).IsGreaterThan(0);
}
}
}
Or more elegantly:
[Test]
public async Task Validate_All_With_Assertion()
{
var users = GetUsers();
await Assert.That(users).All(u =>
!string.IsNullOrEmpty(u.Name) && u.Age > 0
);
}
Find and Assertâ
[Test]
public async Task Find_And_Assert()
{
var users = GetUsers();
var admin = await Assert.That(users)
.Contains(u => u.Role == "Admin");
await Assert.That(admin.Permissions).IsNotEmpty();
}
See Alsoâ
- Dictionaries - Dictionary-specific assertions
- Strings - String collections
- Equality & Comparison - Item comparison