diff --git a/TUnit.Assertions.Tests/StringLengthAssertionTests.cs b/TUnit.Assertions.Tests/StringLengthAssertionTests.cs new file mode 100644 index 0000000000..87fd7e2ae2 --- /dev/null +++ b/TUnit.Assertions.Tests/StringLengthAssertionTests.cs @@ -0,0 +1,164 @@ +using TUnit.Assertions.Exceptions; + +namespace TUnit.Assertions.Tests; + +public class StringLengthAssertionTests +{ + [Test] + public async Task Length_IsEqualTo_Passes_When_Length_Matches() + { + var str = "Hello"; + await Assert.That(str).Length().IsEqualTo(5); + } + + [Test] + public async Task Length_IsEqualTo_Fails_When_Length_DoesNotMatch() + { + var str = "Hello"; + await Assert.That(async () => await Assert.That(str).Length().IsEqualTo(10)) + .Throws() + .And.HasMessageContaining("10"); + } + + [Test] + public async Task Length_IsGreaterThan_Passes_When_Length_IsGreater() + { + var str = "Hello, World!"; + await Assert.That(str).Length().IsGreaterThan(5); + } + + [Test] + public async Task Length_IsGreaterThan_Fails_When_Length_IsNotGreater() + { + var str = "Hi"; + await Assert.That(async () => await Assert.That(str).Length().IsGreaterThan(5)) + .Throws() + .And.HasMessageContaining("greater than") + .And.HasMessageContaining("5"); + } + + [Test] + public async Task Length_IsLessThan_Passes_When_Length_IsLess() + { + var str = "Hi"; + await Assert.That(str).Length().IsLessThan(10); + } + + [Test] + public async Task Length_IsLessThan_Fails_When_Length_IsNotLess() + { + var str = "Hello, World!"; + await Assert.That(async () => await Assert.That(str).Length().IsLessThan(5)) + .Throws() + .And.HasMessageContaining("less than") + .And.HasMessageContaining("5"); + } + + [Test] + public async Task Length_IsGreaterThanOrEqualTo_Passes_When_Equal() + { + var str = "Hello"; + await Assert.That(str).Length().IsGreaterThanOrEqualTo(5); + } + + [Test] + public async Task Length_IsGreaterThanOrEqualTo_Passes_When_Greater() + { + var str = "Hello, World!"; + await Assert.That(str).Length().IsGreaterThanOrEqualTo(5); + } + + [Test] + public async Task Length_IsGreaterThanOrEqualTo_Fails_When_Less() + { + var str = "Hi"; + await Assert.That(async () => await Assert.That(str).Length().IsGreaterThanOrEqualTo(5)) + .Throws() + .And.HasMessageContaining("greater than or equal to") + .And.HasMessageContaining("5"); + } + + [Test] + public async Task Length_IsLessThanOrEqualTo_Passes_When_Equal() + { + var str = "Hello"; + await Assert.That(str).Length().IsLessThanOrEqualTo(5); + } + + [Test] + public async Task Length_IsLessThanOrEqualTo_Passes_When_Less() + { + var str = "Hi"; + await Assert.That(str).Length().IsLessThanOrEqualTo(5); + } + + [Test] + public async Task Length_IsLessThanOrEqualTo_Fails_When_Greater() + { + var str = "Hello, World!"; + await Assert.That(async () => await Assert.That(str).Length().IsLessThanOrEqualTo(5)) + .Throws() + .And.HasMessageContaining("less than or equal to") + .And.HasMessageContaining("5"); + } + + [Test] + public async Task Length_IsBetween_Passes_When_InRange() + { + var str = "Hello"; + await Assert.That(str).Length().IsBetween(1, 10); + } + + [Test] + public async Task Length_IsBetween_Fails_When_OutOfRange() + { + var str = "Hello, World!"; + await Assert.That(async () => await Assert.That(str).Length().IsBetween(1, 5)) + .Throws() + .And.HasMessageContaining("between"); + } + + [Test] + public async Task Length_IsPositive_Passes_For_NonEmptyString() + { + var str = "Hello"; + await Assert.That(str).Length().IsPositive(); + } + + [Test] + public async Task Length_IsGreaterThanOrEqualTo_Zero_Passes_Always() + { + var str = ""; + await Assert.That(str).Length().IsGreaterThanOrEqualTo(0); + } + + [Test] + public async Task Length_IsZero_Passes_For_EmptyString() + { + var str = ""; + await Assert.That(str).Length().IsZero(); + } + + [Test] + public async Task Length_IsNotZero_Passes_For_NonEmptyString() + { + var str = "Hello"; + await Assert.That(str).Length().IsNotZero(); + } + + [Test] + public async Task Length_WithNullString_Returns_Zero() + { + string? str = null; + await Assert.That(str).Length().IsZero(); + } + + [Test] + public async Task Length_Chained_With_And() + { + var str = "Hello"; + await Assert.That(str) + .Length().IsGreaterThan(3) + .And.IsLessThan(10); + } +} diff --git a/TUnit.Assertions/Conditions/StringLengthValueAssertion.cs b/TUnit.Assertions/Conditions/StringLengthValueAssertion.cs new file mode 100644 index 0000000000..faf24a95dc --- /dev/null +++ b/TUnit.Assertions/Conditions/StringLengthValueAssertion.cs @@ -0,0 +1,16 @@ +using TUnit.Assertions.Core; + +namespace TUnit.Assertions.Conditions; + +/// +/// Assertion that evaluates the length of a string and provides numeric assertions on that length. +/// Implements IAssertionSource<int> to enable all numeric assertion methods. +/// Example: await Assert.That(str).Length().IsGreaterThan(5); +/// +public class StringLengthValueAssertion : Sources.ValueAssertion +{ + public StringLengthValueAssertion(AssertionContext stringContext) + : base(stringContext.Map(s => s?.Length ?? 0)) + { + } +} diff --git a/TUnit.Assertions/Extensions/AssertionExtensions.cs b/TUnit.Assertions/Extensions/AssertionExtensions.cs index 8287c6ebdd..d065942ad1 100644 --- a/TUnit.Assertions/Extensions/AssertionExtensions.cs +++ b/TUnit.Assertions/Extensions/AssertionExtensions.cs @@ -683,10 +683,22 @@ private static string GetMemberPath(Expression 0 ? string.Join(".", parts) : "Unknown"; } + /// + /// Gets the length of the string as an integer for numeric assertions. + /// Example: await Assert.That(str).Length().IsGreaterThan(5); + /// + public static StringLengthValueAssertion Length( + this IAssertionSource source) + { + source.Context.ExpressionBuilder.Append(".Length()"); + return new StringLengthValueAssertion(source.Context); + } + /// /// Returns a wrapper for string length assertions. /// Example: await Assert.That(str).HasLength().EqualTo(5); /// + [Obsolete("Use Length() instead, which provides all numeric assertion methods. Example: Assert.That(str).Length().IsGreaterThan(5)")] public static LengthWrapper HasLength( this IAssertionSource source) { @@ -698,6 +710,7 @@ public static LengthWrapper HasLength( /// Asserts that the string has the expected length. /// Example: await Assert.That(str).HasLength(5); /// + [Obsolete("Use Length().IsEqualTo(expectedLength) instead.")] public static StringLengthAssertion HasLength( this IAssertionSource source, int expectedLength, diff --git a/TUnit.Assertions/Sources/CollectionAssertionBase.cs b/TUnit.Assertions/Sources/CollectionAssertionBase.cs index 233e954ca4..d6e0ee81f4 100644 --- a/TUnit.Assertions/Sources/CollectionAssertionBase.cs +++ b/TUnit.Assertions/Sources/CollectionAssertionBase.cs @@ -109,6 +109,7 @@ public CollectionContainsPredicateAssertion Contains( /// This instance method enables calling HasCount with proper type inference. /// Example: await Assert.That(list).HasCount(5); /// + [Obsolete("Use Count().IsEqualTo(expectedCount) instead.")] public CollectionCountAssertion HasCount( int expectedCount, [CallerArgumentExpression(nameof(expectedCount))] string? expression = null) @@ -122,6 +123,7 @@ public CollectionCountAssertion HasCount( /// This enables the pattern: .HasCount().GreaterThan(5) /// Example: await Assert.That(list).HasCount().EqualTo(5); /// + [Obsolete("Use Count() instead, which provides all numeric assertion methods. Example: Assert.That(list).Count().IsGreaterThan(5)")] public CountWrapper HasCount() { Context.ExpressionBuilder.Append(".HasCount()"); diff --git a/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet10_0.verified.txt b/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet10_0.verified.txt index 9714184f79..24dee33ea7 100644 --- a/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet10_0.verified.txt +++ b/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet10_0.verified.txt @@ -1115,6 +1115,10 @@ namespace .Conditions protected override .<.> CheckAsync(. metadata) { } protected override string GetExpectation() { } } + public class StringLengthValueAssertion : . + { + public StringLengthValueAssertion(. stringContext) { } + } public class StringMatchesAssertion : .<..RegexMatchCollection> { public StringMatchesAssertion(. context, . regex) { } @@ -1578,7 +1582,10 @@ namespace .Extensions public static . EqualTo(this . source, TValue? expected, [.("expected")] string? expression = null) { } public static ..HasFlagAssertion HasFlag(this . source, TEnum expectedFlag, [.("expectedFlag")] string? expression = null) where TEnum : struct, { } + [("Use Length() instead, which provides all numeric assertion methods. Example: Asse" + + "(str).Length().IsGreaterThan(5)")] public static ..LengthWrapper HasLength(this . source) { } + [("Use Length().IsEqualTo(expectedLength) instead.")] public static . HasLength(this . source, int expectedLength, [.("expectedLength")] string? expression = null) { } public static . HasMessageContaining(this . source, string expectedSubstring, [.("expectedSubstring")] string? expression = null) where TException : { } @@ -1633,6 +1640,7 @@ namespace .Extensions where TValue : { } public static . IsPositive(this . source) where TValue : struct, { } + public static . Length(this . source) { } [.(1)] public static . Member(this . source, .<>> memberSelector, <.<., TItem>, .<.>> assertions) { } [.("Uses reflection for legacy compatibility. For AOT compatibility, use the Member DoesNotContain( predicate, [.("predicate")] string? expression = null) { } public . DoesNotContain(TItem expected, [.("expected")] string? expression = null) { } protected override string GetExpectation() { } + [("Use Count() instead, which provides all numeric assertion methods. Example: Asser" + + "(list).Count().IsGreaterThan(5)")] public ..CountWrapper HasCount() { } + [("Use Count().IsEqualTo(expectedCount) instead.")] public . HasCount(int expectedCount, [.("expectedCount")] string? expression = null) { } public . HasDistinctItems() { } public . HasSingleItem() { } diff --git a/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet8_0.verified.txt b/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet8_0.verified.txt index 665faeb994..9f3c6cccc7 100644 --- a/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet8_0.verified.txt +++ b/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet8_0.verified.txt @@ -1112,6 +1112,10 @@ namespace .Conditions protected override .<.> CheckAsync(. metadata) { } protected override string GetExpectation() { } } + public class StringLengthValueAssertion : . + { + public StringLengthValueAssertion(. stringContext) { } + } public class StringMatchesAssertion : .<..RegexMatchCollection> { public StringMatchesAssertion(. context, . regex) { } @@ -1575,7 +1579,10 @@ namespace .Extensions public static . EqualTo(this . source, TValue? expected, [.("expected")] string? expression = null) { } public static ..HasFlagAssertion HasFlag(this . source, TEnum expectedFlag, [.("expectedFlag")] string? expression = null) where TEnum : struct, { } + [("Use Length() instead, which provides all numeric assertion methods. Example: Asse" + + "(str).Length().IsGreaterThan(5)")] public static ..LengthWrapper HasLength(this . source) { } + [("Use Length().IsEqualTo(expectedLength) instead.")] public static . HasLength(this . source, int expectedLength, [.("expectedLength")] string? expression = null) { } public static . HasMessageContaining(this . source, string expectedSubstring, [.("expectedSubstring")] string? expression = null) where TException : { } @@ -1630,6 +1637,7 @@ namespace .Extensions where TValue : { } public static . IsPositive(this . source) where TValue : struct, { } + public static . Length(this . source) { } public static . Member(this . source, .<>> memberSelector, <.<., TItem>, .<.>> assertions) { } [.("Uses reflection for legacy compatibility. For AOT compatibility, use the Member overload with strongly-typed assertions.")] @@ -4330,7 +4338,10 @@ namespace .Sources public . DoesNotContain( predicate, [.("predicate")] string? expression = null) { } public . DoesNotContain(TItem expected, [.("expected")] string? expression = null) { } protected override string GetExpectation() { } + [("Use Count() instead, which provides all numeric assertion methods. Example: Asser" + + "(list).Count().IsGreaterThan(5)")] public ..CountWrapper HasCount() { } + [("Use Count().IsEqualTo(expectedCount) instead.")] public . HasCount(int expectedCount, [.("expectedCount")] string? expression = null) { } public . HasDistinctItems() { } public . HasSingleItem() { } diff --git a/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet9_0.verified.txt b/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet9_0.verified.txt index c8665a0b6a..ea9a094de1 100644 --- a/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet9_0.verified.txt +++ b/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet9_0.verified.txt @@ -1115,6 +1115,10 @@ namespace .Conditions protected override .<.> CheckAsync(. metadata) { } protected override string GetExpectation() { } } + public class StringLengthValueAssertion : . + { + public StringLengthValueAssertion(. stringContext) { } + } public class StringMatchesAssertion : .<..RegexMatchCollection> { public StringMatchesAssertion(. context, . regex) { } @@ -1578,7 +1582,10 @@ namespace .Extensions public static . EqualTo(this . source, TValue? expected, [.("expected")] string? expression = null) { } public static ..HasFlagAssertion HasFlag(this . source, TEnum expectedFlag, [.("expectedFlag")] string? expression = null) where TEnum : struct, { } + [("Use Length() instead, which provides all numeric assertion methods. Example: Asse" + + "(str).Length().IsGreaterThan(5)")] public static ..LengthWrapper HasLength(this . source) { } + [("Use Length().IsEqualTo(expectedLength) instead.")] public static . HasLength(this . source, int expectedLength, [.("expectedLength")] string? expression = null) { } public static . HasMessageContaining(this . source, string expectedSubstring, [.("expectedSubstring")] string? expression = null) where TException : { } @@ -1633,6 +1640,7 @@ namespace .Extensions where TValue : { } public static . IsPositive(this . source) where TValue : struct, { } + public static . Length(this . source) { } [.(1)] public static . Member(this . source, .<>> memberSelector, <.<., TItem>, .<.>> assertions) { } [.("Uses reflection for legacy compatibility. For AOT compatibility, use the Member DoesNotContain( predicate, [.("predicate")] string? expression = null) { } public . DoesNotContain(TItem expected, [.("expected")] string? expression = null) { } protected override string GetExpectation() { } + [("Use Count() instead, which provides all numeric assertion methods. Example: Asser" + + "(list).Count().IsGreaterThan(5)")] public ..CountWrapper HasCount() { } + [("Use Count().IsEqualTo(expectedCount) instead.")] public . HasCount(int expectedCount, [.("expectedCount")] string? expression = null) { } public . HasDistinctItems() { } public . HasSingleItem() { } diff --git a/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.Net4_7.verified.txt b/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.Net4_7.verified.txt index bd832a6fca..6444294fe0 100644 --- a/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.Net4_7.verified.txt +++ b/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.Net4_7.verified.txt @@ -1044,6 +1044,10 @@ namespace .Conditions protected override .<.> CheckAsync(. metadata) { } protected override string GetExpectation() { } } + public class StringLengthValueAssertion : . + { + public StringLengthValueAssertion(. stringContext) { } + } public class StringMatchesAssertion : .<..RegexMatchCollection> { public StringMatchesAssertion(. context, . regex) { } @@ -1476,7 +1480,10 @@ namespace .Extensions public static . EqualTo(this . source, TValue? expected, [.("expected")] string? expression = null) { } public static ..HasFlagAssertion HasFlag(this . source, TEnum expectedFlag, [.("expectedFlag")] string? expression = null) where TEnum : struct, { } + [("Use Length() instead, which provides all numeric assertion methods. Example: Asse" + + "(str).Length().IsGreaterThan(5)")] public static ..LengthWrapper HasLength(this . source) { } + [("Use Length().IsEqualTo(expectedLength) instead.")] public static . HasLength(this . source, int expectedLength, [.("expectedLength")] string? expression = null) { } public static . HasMessageContaining(this . source, string expectedSubstring, [.("expectedSubstring")] string? expression = null) where TException : { } @@ -1521,6 +1528,7 @@ namespace .Extensions where TValue : { } public static . IsPositive(this . source) where TValue : struct, { } + public static . Length(this . source) { } public static . Member(this . source, .<>> memberSelector, <.<., TItem>, .<.>> assertions) { } public static . Member(this . source, .<>> memberSelector, <.<., TItem>, object> assertions) { } public static . Member(this . source, .<> memberSelector, <., .> assertions) { } @@ -3819,7 +3827,10 @@ namespace .Sources public . DoesNotContain( predicate, [.("predicate")] string? expression = null) { } public . DoesNotContain(TItem expected, [.("expected")] string? expression = null) { } protected override string GetExpectation() { } + [("Use Count() instead, which provides all numeric assertion methods. Example: Asser" + + "(list).Count().IsGreaterThan(5)")] public ..CountWrapper HasCount() { } + [("Use Count().IsEqualTo(expectedCount) instead.")] public . HasCount(int expectedCount, [.("expectedCount")] string? expression = null) { } public . HasDistinctItems() { } public . HasSingleItem() { } diff --git a/docs/docs/advanced/performance-best-practices.md b/docs/docs/advanced/performance-best-practices.md index 6818badae3..3e7f0abb2f 100644 --- a/docs/docs/advanced/performance-best-practices.md +++ b/docs/docs/advanced/performance-best-practices.md @@ -222,8 +222,8 @@ public class PerformantTests ```csharp // ❌ Bad: Expensive operation in assertion await Assert.That(await GetAllUsersFromDatabase()) - .HasCount() - .EqualTo(1000); + .Count() + .IsEqualTo(1000); // ✅ Good: Use efficient queries var userCount = await GetUserCountFromDatabase(); @@ -246,7 +246,7 @@ public async Task EfficientValidation() } // More expensive validations only if needed - await Assert.That(result.Items).HasCount().GreaterThan(0); + await Assert.That(result.Items).Count().IsGreaterThan(0); } ``` diff --git a/docs/docs/assertions/awaiting.md b/docs/docs/assertions/awaiting.md index 2185db045c..540a19102c 100644 --- a/docs/docs/assertions/awaiting.md +++ b/docs/docs/assertions/awaiting.md @@ -90,7 +90,7 @@ public async Task ComplexCollectionAssertions() // Assert multiple conditions on a collection await Assert.That(orders) - .HasCount().IsGreaterThan(0) + .Count().IsGreaterThan(0) .And.Contains(o => o.Status == OrderStatus.Completed) .And.DoesNotContain(o => o.Total < 0) .And.HasDistinctItems(); @@ -146,7 +146,7 @@ public async Task DetailedExceptionAssertions() var exception = await Assert.That(() => ParallelOperationAsync()) .Throws(); - await Assert.That(exception.InnerExceptions).HasCount(3); + await Assert.That(exception.InnerExceptions).Count().IsEqualTo(3); await Assert.That(exception.InnerExceptions).All(e => e is TaskCanceledException); } ``` @@ -169,7 +169,7 @@ public async Task CustomAssertionConditions() // Combine built-in and custom assertions await Assert.That(measurements) - .HasCount().GreaterThan(100) + .Count().IsGreaterThan(100) .And.All(m => m > 0) .And.Satisfies(IsNormallyDistributed, "Data should be normally distributed"); } @@ -242,7 +242,7 @@ public async Task StringPatternAssertions() .StartsWith("Report Generated:") .And.Contains("Total Items:") .And.DoesNotContain("null") - .And.HasLength().IsBetween(1000, 5000); + .And.Length().IsBetween(1000, 5000); } ``` @@ -299,7 +299,7 @@ public async Task PerformanceAssertions() await Assert.That(results.Max()) .IsLessThan(500); // No operation over 500ms - await Assert.That(results.Where(r => r > 200).HasCount()) + await Assert.That(results.Where(r => r > 200).Count()) .IsLessThan(5); // Less than 5% over 200ms } ``` diff --git a/docs/docs/assertions/collections.md b/docs/docs/assertions/collections.md index 52f72d5d27..db49665263 100644 --- a/docs/docs/assertions/collections.md +++ b/docs/docs/assertions/collections.md @@ -98,7 +98,7 @@ public async Task Collection_Does_Not_Contain_Matching() ## Count Assertions -### HasCount +### Count Tests that a collection has an exact count: @@ -108,7 +108,7 @@ public async Task Collection_Has_Count() { var numbers = new[] { 1, 2, 3, 4, 5 }; - await Assert.That(numbers).HasCount(5); + await Assert.That(numbers).Count().IsEqualTo(5); } ``` @@ -123,11 +123,11 @@ public async Task Count_With_Comparison() var numbers = new[] { 1, 2, 3, 4, 5 }; await Assert.That(numbers) - .HasCount().EqualTo(5); + .Count().IsEqualTo(5); await Assert.That(numbers) - .HasCount().GreaterThan(3) - .And.HasCount().LessThan(10); + .Count().IsGreaterThan(3) + .And.Count().IsLessThan(10); } ``` @@ -160,7 +160,7 @@ public async Task Collection_Is_Empty() var empty = new List(); await Assert.That(empty).IsEmpty(); - await Assert.That(empty).HasCount(0); + await Assert.That(empty).Count().IsEqualTo(0); } ``` @@ -638,7 +638,7 @@ public async Task Filter_And_Assert() var evens = numbers.Where(n => n % 2 == 0).ToArray(); await Assert.That(evens) - .HasCount(5) + .Count().IsEqualTo(5) .And.All(n => n % 2 == 0); } ``` @@ -659,7 +659,7 @@ public async Task LINQ_Query_Results() var adults = users.Where(u => u.Age >= 18).ToArray(); await Assert.That(adults) - .HasCount(3) + .Count().IsEqualTo(3) .And.All(u => u.Age >= 18); } ``` @@ -708,7 +708,7 @@ public async Task Map_And_Verify() var names = users.Select(u => u.Name).ToArray(); await Assert.That(names) - .HasCount(2) + .Count().IsEqualTo(2) .And.Contains("Alice") .And.Contains("Bob") .And.All(name => !string.IsNullOrEmpty(name)); @@ -745,12 +745,12 @@ public async Task Nested_Collections() new[] { 7, 8, 9 } }; - await Assert.That(matrix).HasCount(3); + 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).HasCount(9); + await Assert.That(flattened).Count().IsEqualTo(9); } ``` @@ -768,7 +768,7 @@ public async Task Collection_Of_Collections() }; await Assert.That(groups) - .HasCount(3) + .Count().IsEqualTo(3) .And.All(group => group.Count > 0); } ``` @@ -783,7 +783,7 @@ public async Task Chained_Collection_Assertions() await Assert.That(numbers) .IsNotEmpty() - .And.HasCount(5) + .And.Count().IsEqualTo(5) .And.Contains(3) .And.DoesNotContain(10) .And.IsInOrder() @@ -807,7 +807,7 @@ public async Task Materialize_Before_Multiple_Assertions() // Materialize once to avoid re-execution var materialized = query.ToArray(); - await Assert.That(materialized).HasCount().GreaterThan(1000); + await Assert.That(materialized).Count().IsGreaterThan(1000); await Assert.That(materialized).Contains(100); await Assert.That(materialized).All(n => n % 2 == 0); } @@ -822,7 +822,7 @@ public async Task HashSet_Assertions() var set = new HashSet { 1, 2, 3, 4, 5 }; await Assert.That(set) - .HasCount(5) + .Count().IsEqualTo(5) .And.Contains(3) .And.HasDistinctItems(); } diff --git a/docs/docs/assertions/dictionaries.md b/docs/docs/assertions/dictionaries.md index 938a723f6e..24320dffa8 100644 --- a/docs/docs/assertions/dictionaries.md +++ b/docs/docs/assertions/dictionaries.md @@ -104,7 +104,7 @@ public async Task Dictionary_Count() ["c"] = 3 }; - await Assert.That(dict).HasCount(3); + await Assert.That(dict).Count().IsEqualTo(3); } ``` @@ -223,7 +223,7 @@ public async Task Lookup_Table() }; await Assert.That(statusCodes) - .HasCount(3) + .Count().IsEqualTo(3) .And.ContainsKey(200) .And.ContainsValue("OK"); } @@ -243,7 +243,7 @@ public async Task Cache_Contains_Entry() await Assert.That(cache) .ContainsKey("user:123") - .And.HasCount(2) + .And.Count().IsEqualTo(2) .And.IsNotEmpty(); } ``` @@ -306,7 +306,7 @@ public async Task Dictionary_Keys() var keys = dict.Keys; await Assert.That(keys) - .HasCount(3) + .Count().IsEqualTo(3) .And.Contains("a") .And.Contains("b") .And.Contains("c"); @@ -329,7 +329,7 @@ public async Task Dictionary_Values() var values = dict.Values; await Assert.That(values) - .HasCount(3) + .Count().IsEqualTo(3) .And.Contains(1) .And.Contains(2) .And.All(v => v > 0); @@ -376,7 +376,7 @@ public async Task Chained_Dictionary_Assertions() await Assert.That(dict) .IsNotEmpty() - .And.HasCount(3) + .And.Count().IsEqualTo(3) .And.ContainsKey("apple") .And.ContainsKey("banana") .And.ContainsValue(2) @@ -397,7 +397,7 @@ public async Task Concurrent_Dictionary() concurrent.TryAdd("b", 2); await Assert.That(concurrent) - .HasCount(2) + .Count().IsEqualTo(2) .And.ContainsKey("a"); } ``` @@ -412,7 +412,7 @@ public async Task ReadOnly_Dictionary() var readOnly = new ReadOnlyDictionary(dict); await Assert.That(readOnly) - .HasCount(1) + .Count().IsEqualTo(1) .And.ContainsKey("a"); } ``` diff --git a/docs/docs/assertions/equality-and-comparison.md b/docs/docs/assertions/equality-and-comparison.md index f20912b79f..f2b14bc868 100644 --- a/docs/docs/assertions/equality-and-comparison.md +++ b/docs/docs/assertions/equality-and-comparison.md @@ -54,7 +54,7 @@ public async Task Using_EqualTo_Alias() var numbers = new[] { 1, 2, 3 }; await Assert.That(numbers) - .HasCount().EqualTo(3) + .Count().IsEqualTo(3) .And.Contains(2); } ``` diff --git a/docs/docs/assertions/extensibility/extensibility-chaining-and-converting.md b/docs/docs/assertions/extensibility/extensibility-chaining-and-converting.md index befc48a581..59e748ad0f 100644 --- a/docs/docs/assertions/extensibility/extensibility-chaining-and-converting.md +++ b/docs/docs/assertions/extensibility/extensibility-chaining-and-converting.md @@ -185,4 +185,4 @@ public class HasDetailAssertion : Assertion TUnit includes several built-in examples of type conversion assertions: - `WhenParsedInto()` - Converts a string to a parsed type (e.g., `await Assert.That("123").WhenParsedInto().IsEqualTo(123)`) -- `IsTypeOf()` - Converts to a specific type (e.g., `await Assert.That(obj).IsTypeOf().HasLength(5)`) +- `IsTypeOf()` - Converts to a specific type (e.g., `await Assert.That(obj).IsTypeOf().Length().IsEqualTo(5)`) diff --git a/docs/docs/assertions/getting-started.md b/docs/docs/assertions/getting-started.md index 67c466360b..a078a9af9b 100644 --- a/docs/docs/assertions/getting-started.md +++ b/docs/docs/assertions/getting-started.md @@ -63,7 +63,7 @@ await Assert.That(input).IsNotEmpty(); ```csharp await Assert.That(numbers).Contains(42); -await Assert.That(items).HasCount(5); +await Assert.That(items).Count().IsEqualTo(5); await Assert.That(list).IsNotEmpty(); await Assert.That(values).All(x => x > 0); ``` @@ -99,8 +99,8 @@ Combine multiple assertions on the same value using `.And`: await Assert.That(username) .IsNotNull() .And.IsNotEmpty() - .And.HasLength().GreaterThan(3) - .And.HasLength().LessThan(20); + .And.Length().IsGreaterThan(3) + .And.Length().IsLessThan(20); ``` Use `.Or` when any condition can be true: @@ -153,7 +153,7 @@ Collections have rich assertion support: var numbers = new[] { 1, 2, 3, 4, 5 }; // Count and emptiness -await Assert.That(numbers).HasCount(5); +await Assert.That(numbers).Count().IsEqualTo(5); await Assert.That(numbers).IsNotEmpty(); // Membership @@ -397,7 +397,7 @@ Now that you understand the basics, explore specific assertion types: | Null/Default | `IsNull()`, `IsNotNull()`, `IsDefault()` | | Boolean | `IsTrue()`, `IsFalse()` | | Strings | `Contains()`, `StartsWith()`, `Matches()` | -| Collections | `Contains()`, `HasCount()`, `All()`, `Any()` | +| Collections | `Contains()`, `Count()`, `All()`, `Any()` | | Exceptions | `Throws()`, `ThrowsNothing()` | | Types | `IsTypeOf()`, `IsAssignableTo()` | | Async | `CompletesWithin()`, async exception testing | diff --git a/docs/docs/assertions/member-assertions.md b/docs/docs/assertions/member-assertions.md index ea0b7e4e4d..c0d8d7cec4 100644 --- a/docs/docs/assertions/member-assertions.md +++ b/docs/docs/assertions/member-assertions.md @@ -72,7 +72,7 @@ public async Task ComplexMemberAssertions() await Assert.That(team) .Member(t => t.Name, name => name.StartsWith("Team")) .And.Member(t => t.Members, members => members - .HasCount().IsGreaterThan(0) + .Count().IsGreaterThan(0) .And.All(m => m.IsActive) .And.Any(m => m.Role == "Lead")) .And.Member(t => t.CreatedDate, date => date @@ -134,7 +134,7 @@ public async Task NestedObjectAssertions() .And.Member(c => c.Address.City, city => city.IsEqualTo("Seattle")) .And.Member(c => c.Address.ZipCode, zip => zip.Matches(@"^\d{5}$")) .And.Member(c => c.Employees, employees => employees - .HasCount().IsBetween(100, 500) + .Count().IsBetween(100, 500) .And.All(e => e.Email.EndsWith("@techcorp.com"))); } ``` diff --git a/docs/docs/assertions/null-and-default.md b/docs/docs/assertions/null-and-default.md index c0570f6fbd..b2a241aa3a 100644 --- a/docs/docs/assertions/null-and-default.md +++ b/docs/docs/assertions/null-and-default.md @@ -69,7 +69,7 @@ public async Task Chained_After_Null_Check() await Assert.That(input) .IsNotNull() .And.IsNotEmpty() // Compiler knows input is not null - .And.HasLength().GreaterThan(5); + .And.Length().IsGreaterThan(5); } ``` diff --git a/docs/docs/assertions/regex-assertions.md b/docs/docs/assertions/regex-assertions.md index 6b2d9b1261..048aefc022 100644 --- a/docs/docs/assertions/regex-assertions.md +++ b/docs/docs/assertions/regex-assertions.md @@ -108,13 +108,13 @@ public async Task PositionAndLengthAssertions() // Assert that match has specific length await Assert.That(text) .Matches(pattern) - .HasLength(3); + .Length().IsEqualTo(3); // Combine with group assertions await Assert.That(text) .Matches(pattern) .AtIndex(12) - .And.HasLength(3); + .And.Length().IsEqualTo(3); } ``` @@ -149,7 +149,7 @@ public async Task ProductCodeValidation() .Matches(pattern) .And.Group("code", code => code.StartsWith("ABC")) .And.Group("price", price => price.Contains(".99")) - .And.Group("stock", stock => stock.HasLength(2)); + .And.Group("stock", stock => stock.Length().IsEqualTo(2)); } ``` @@ -259,9 +259,9 @@ public async Task CompleteEmailValidation() .And.Group("local", local => local.StartsWith("john")) .And.Group("subdomain", sub => sub.IsEqualTo("mail")) .And.Group("domain", domain => domain.IsEqualTo("example")) - .And.Group("tld", tld => tld.HasLength(3)) + .And.Group("tld", tld => tld.Length().IsEqualTo(3)) .And.AtIndex(0) - .And.HasLength(email.Length); + .And.Length().IsEqualTo(email.Length); } ``` diff --git a/docs/docs/assertions/string.md b/docs/docs/assertions/string.md index 7ffadc6bd6..6d9336f4a0 100644 --- a/docs/docs/assertions/string.md +++ b/docs/docs/assertions/string.md @@ -291,7 +291,7 @@ public async Task String_Is_Not_Empty() } ``` -### HasLength +### Length Tests that a string has a specific length: @@ -301,7 +301,7 @@ public async Task String_Has_Length() { var code = "ABC123"; - await Assert.That(code).HasLength(6); + await Assert.That(code).Length().IsEqualTo(6); } ``` @@ -314,12 +314,12 @@ public async Task Length_With_Comparison() var username = "alice"; await Assert.That(username) - .HasLength().GreaterThan(3) - .And.HasLength().LessThan(20); + .Length().IsGreaterThan(3) + .And.Length().IsLessThan(20); } ``` -Or more concisely: +Using `IsBetween`: ```csharp [Test] @@ -327,7 +327,7 @@ public async Task Length_Range() { var username = "alice"; - await Assert.That(username.Length).IsBetween(3, 20); + await Assert.That(username).Length().IsBetween(3, 20); } ``` @@ -459,8 +459,8 @@ public async Task Validate_Username() var username = "alice_123"; await Assert.That(username) - .HasLength().GreaterThanOrEqualTo(3) - .And.HasLength().LessThanOrEqualTo(20) + .Length().IsGreaterThanOrEqualTo(3) + .And.Length().IsLessThanOrEqualTo(20) .And.Matches(@"^[a-zA-Z0-9_]+$") .And.DoesNotContain(" "); } @@ -475,7 +475,7 @@ public async Task Validate_Password() var password = "SecureP@ss123"; await Assert.That(password) - .HasLength().GreaterThanOrEqualTo(8) + .Length().IsGreaterThanOrEqualTo(8) .And.Matches(@"[A-Z]") // Has uppercase .And.Matches(@"[a-z]") // Has lowercase .And.Matches(@"\d") // Has digit @@ -600,7 +600,7 @@ public async Task Chained_String_Assertions() .And.Contains("World") .And.StartsWith("Hello") .And.EndsWith("!") - .And.HasLength(13); + .And.Length().IsEqualTo(13); } ``` diff --git a/docs/docs/getting-started/writing-your-first-test.md b/docs/docs/getting-started/writing-your-first-test.md index ef503ae7d3..346ce02d08 100644 --- a/docs/docs/getting-started/writing-your-first-test.md +++ b/docs/docs/getting-started/writing-your-first-test.md @@ -188,7 +188,7 @@ public class StringTests // Assert await Assert.That(result).IsEqualTo("HELLO"); - await Assert.That(result).HasLength(5); + await Assert.That(result).Length().IsEqualTo(5); await Assert.That(result.StartsWith("HE")).IsTrue(); } } diff --git a/docs/docs/guides/cookbook.md b/docs/docs/guides/cookbook.md index 95b0eaad8d..6721c8df26 100644 --- a/docs/docs/guides/cookbook.md +++ b/docs/docs/guides/cookbook.md @@ -89,7 +89,7 @@ public class OrderServiceTests }); await Assert.That(order.Id).IsGreaterThan(0); - await Assert.That(order.Items).HasCount().EqualTo(1); + await Assert.That(order.Items).Count().IsEqualTo(1); } } ``` @@ -616,7 +616,7 @@ public class OrderRepositoryIntegrationTests var loaded = await repository.GetWithItemsAsync(order.Id); await Assert.That(loaded).IsNotNull(); - await Assert.That(loaded!.Items).HasCount().EqualTo(2); + await Assert.That(loaded!.Items).Count().IsEqualTo(2); } } ``` diff --git a/docs/docs/migration/nunit.md b/docs/docs/migration/nunit.md index ad278b1fcd..1e996268a1 100644 --- a/docs/docs/migration/nunit.md +++ b/docs/docs/migration/nunit.md @@ -137,7 +137,7 @@ await Assert.That(actual).IsEqualTo(expected); await Assert.That(value).IsTrue(); await Assert.That(value).IsNull(); await Assert.That(text).Contains("substring"); -await Assert.That(collection).HasCount().EqualTo(5); +await Assert.That(collection).Count().IsEqualTo(5); ``` ### Collection Assertions @@ -610,7 +610,7 @@ public async Task ComplexAssertions() await Assert.That(text).Matches(@"^Hello"); // Collection assertions - await Assert.That(list).HasCount().EqualTo(5); + await Assert.That(list).Count().IsEqualTo(5); await Assert.That(list).Contains(3); await Assert.That(list).AllSatisfy(x => x > 0); await Assert.That(list).IsInAscendingOrder(); diff --git a/docs/docs/migration/xunit.md b/docs/docs/migration/xunit.md index 97a96bb507..f452d62eb7 100644 --- a/docs/docs/migration/xunit.md +++ b/docs/docs/migration/xunit.md @@ -790,7 +790,7 @@ public async Task Collection_Assertions() await Assert.That(list).DoesNotContain(5); await Assert.That(Array.Empty()).IsEmpty(); await Assert.That(list).IsNotEmpty(); - await Assert.That(list).HasCount().EqualTo(3); + await Assert.That(list).Count().IsEqualTo(3); } ``` diff --git a/docs/docs/troubleshooting.md b/docs/docs/troubleshooting.md index b0f44ddad1..4dfa0bb1ab 100644 --- a/docs/docs/troubleshooting.md +++ b/docs/docs/troubleshooting.md @@ -463,7 +463,7 @@ await Assert.That(actual).IsEquivalentTo(expected, CollectionOrdering.Matching); Or assert on elements individually: ```csharp -await Assert.That(actual).HasCount().EqualTo(expected.Length); +await Assert.That(actual).Count().IsEqualTo(expected.Length); for (int i = 0; i < expected.Length; i++) { await Assert.That(actual[i]).IsEqualTo(expected[i]); @@ -483,7 +483,7 @@ var expected = new[] await Assert.That(actual).IsEquivalentTo(expected); // More reliable - assert on properties -await Assert.That(actual).HasCount().EqualTo(2); +await Assert.That(actual).Count().IsEqualTo(2); await Assert.That(actual[0].Name).IsEqualTo("Alice"); await Assert.That(actual[1].Name).IsEqualTo("Bob"); @@ -502,7 +502,7 @@ var actual = new[] { (1, "a"), (2, "b") }; // await Assert.That(actual).IsEquivalentTo(expected); // Workaround - assert individual elements -await Assert.That(actual).HasCount().EqualTo(2); +await Assert.That(actual).Count().IsEqualTo(2); await Assert.That(actual[0]).IsEqualTo((1, "a")); await Assert.That(actual[1]).IsEqualTo((2, "b")); ``` @@ -516,7 +516,7 @@ var list = new List { 1, 2, 3 }; await Assert.That(list).IsEquivalentTo(new[] { 1, 2, 3 }); // Check specific properties -await Assert.That(list).HasCount().EqualTo(3); +await Assert.That(list).Count().IsEqualTo(3); await Assert.That(list).Contains(2); await Assert.That(list).DoesNotContain(5); ```