-
-
Notifications
You must be signed in to change notification settings - Fork 108
feat: add Count method with inner assertion support for collections #4041
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,13 +1,14 @@ | ||||||||||||||||||||||||||||||||
| using System.Collections; | ||||||||||||||||||||||||||||||||
| using TUnit.Assertions.Core; | ||||||||||||||||||||||||||||||||
| using TUnit.Assertions.Sources; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| namespace TUnit.Assertions.Conditions; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| /// <summary> | ||||||||||||||||||||||||||||||||
| /// Assertion that evaluates the count of a collection and provides numeric assertions on that count. | ||||||||||||||||||||||||||||||||
| /// Implements IAssertionSource<int> to enable all numeric assertion methods. | ||||||||||||||||||||||||||||||||
| /// </summary> | ||||||||||||||||||||||||||||||||
| public class CollectionCountValueAssertion<TCollection, TItem> : Sources.ValueAssertion<int> | ||||||||||||||||||||||||||||||||
| public class CollectionCountValueAssertion<TCollection, TItem> : ValueAssertion<int> | ||||||||||||||||||||||||||||||||
| where TCollection : IEnumerable<TItem> | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| public CollectionCountValueAssertion( | ||||||||||||||||||||||||||||||||
|
|
@@ -42,3 +43,63 @@ private static AssertionContext<int> CreateIntContext( | |||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| /// <summary> | ||||||||||||||||||||||||||||||||
| /// Assertion that evaluates the count of items satisfying an inner assertion and provides numeric assertions on that count. | ||||||||||||||||||||||||||||||||
| /// Implements IAssertionSource<int> to enable all numeric assertion methods. | ||||||||||||||||||||||||||||||||
| /// This allows using the full assertion builder (e.g., item => item.IsGreaterThan(10)) instead of a simple predicate. | ||||||||||||||||||||||||||||||||
| /// </summary> | ||||||||||||||||||||||||||||||||
| public class CollectionCountWithAssertionValueAssertion<TCollection, TItem> : ValueAssertion<int> | ||||||||||||||||||||||||||||||||
| where TCollection : IEnumerable<TItem> | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| public CollectionCountWithAssertionValueAssertion( | ||||||||||||||||||||||||||||||||
| AssertionContext<TCollection> collectionContext, | ||||||||||||||||||||||||||||||||
| Func<IAssertionSource<TItem>, Assertion<TItem>?> assertion) | ||||||||||||||||||||||||||||||||
| : base(CreateIntContext(collectionContext, assertion)) | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| private static AssertionContext<int> CreateIntContext( | ||||||||||||||||||||||||||||||||
| AssertionContext<TCollection> collectionContext, | ||||||||||||||||||||||||||||||||
| Func<IAssertionSource<TItem>, Assertion<TItem>?> assertion) | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| return collectionContext.Map<int>(async collection => | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| if (collection == null) | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| return 0; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| int count = 0; | ||||||||||||||||||||||||||||||||
| int index = 0; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| foreach (var item in collection) | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| var itemAssertion = new ValueAssertion<TItem>(item, $"item[{index}]"); | ||||||||||||||||||||||||||||||||
| var resultingAssertion = assertion(itemAssertion); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| if (resultingAssertion != null) | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| try | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| await resultingAssertion.AssertAsync(); | ||||||||||||||||||||||||||||||||
| count++; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| catch | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| // Item did not satisfy the assertion, don't count it | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
Comment on lines
+83
to
+91
|
||||||||||||||||||||||||||||||||
| try | |
| { | |
| await resultingAssertion.AssertAsync(); | |
| count++; | |
| } | |
| catch | |
| { | |
| // Item did not satisfy the assertion, don't count it | |
| } | |
| var result = await resultingAssertion.CheckAsync(); | |
| if (result.IsPassed) | |
| { | |
| count++; | |
| } | |
| // If not passed, don't count it |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
String interpolation in a loop creates a new string for each iteration. Since this expression is only used for potential error messages in the inner assertion and not for the count logic itself, consider if this allocation is necessary in a hot path.
If the expression isn't critical for this use case, you might pass
nullor a constant string to avoid repeated allocations. Alternatively, use a cached format if the expression is needed.