Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 164 additions & 0 deletions TUnit.Assertions.Tests/StringLengthAssertionTests.cs
Original file line number Diff line number Diff line change
@@ -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<TUnitAssertionException>()
.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<TUnitAssertionException>()
.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<TUnitAssertionException>()
.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<TUnitAssertionException>()
.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<TUnitAssertionException>()
.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<TUnitAssertionException>()
.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);
}
}
16 changes: 16 additions & 0 deletions TUnit.Assertions/Conditions/StringLengthValueAssertion.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using TUnit.Assertions.Core;

namespace TUnit.Assertions.Conditions;

/// <summary>
/// Assertion that evaluates the length of a string and provides numeric assertions on that length.
/// Implements IAssertionSource&lt;int&gt; to enable all numeric assertion methods.
/// Example: await Assert.That(str).Length().IsGreaterThan(5);
/// </summary>
public class StringLengthValueAssertion : Sources.ValueAssertion<int>
{
public StringLengthValueAssertion(AssertionContext<string> stringContext)
: base(stringContext.Map<int>(s => s?.Length ?? 0))
{
}
}
13 changes: 13 additions & 0 deletions TUnit.Assertions/Extensions/AssertionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -683,10 +683,22 @@ private static string GetMemberPath<TObject, TMember>(Expression<Func<TObject, T
return parts.Count > 0 ? string.Join(".", parts) : "Unknown";
}

/// <summary>
/// Gets the length of the string as an integer for numeric assertions.
/// Example: await Assert.That(str).Length().IsGreaterThan(5);
/// </summary>
public static StringLengthValueAssertion Length(
this IAssertionSource<string> source)
{
source.Context.ExpressionBuilder.Append(".Length()");
return new StringLengthValueAssertion(source.Context);
}

/// <summary>
/// Returns a wrapper for string length assertions.
/// Example: await Assert.That(str).HasLength().EqualTo(5);
/// </summary>
[Obsolete("Use Length() instead, which provides all numeric assertion methods. Example: Assert.That(str).Length().IsGreaterThan(5)")]
public static LengthWrapper HasLength(
this IAssertionSource<string> source)
{
Expand All @@ -698,6 +710,7 @@ public static LengthWrapper HasLength(
/// Asserts that the string has the expected length.
/// Example: await Assert.That(str).HasLength(5);
/// </summary>
[Obsolete("Use Length().IsEqualTo(expectedLength) instead.")]
public static StringLengthAssertion HasLength(
this IAssertionSource<string> source,
int expectedLength,
Expand Down
2 changes: 2 additions & 0 deletions TUnit.Assertions/Sources/CollectionAssertionBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ public CollectionContainsPredicateAssertion<TCollection, TItem> Contains(
/// This instance method enables calling HasCount with proper type inference.
/// Example: await Assert.That(list).HasCount(5);
/// </summary>
[Obsolete("Use Count().IsEqualTo(expectedCount) instead.")]
public CollectionCountAssertion<TCollection, TItem> HasCount(
int expectedCount,
[CallerArgumentExpression(nameof(expectedCount))] string? expression = null)
Expand All @@ -122,6 +123,7 @@ public CollectionCountAssertion<TCollection, TItem> HasCount(
/// This enables the pattern: .HasCount().GreaterThan(5)
/// Example: await Assert.That(list).HasCount().EqualTo(5);
/// </summary>
[Obsolete("Use Count() instead, which provides all numeric assertion methods. Example: Assert.That(list).Count().IsGreaterThan(5)")]
public CountWrapper<TCollection, TItem> HasCount()
{
Context.ExpressionBuilder.Append(".HasCount()");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1115,6 +1115,10 @@ namespace .Conditions
protected override .<.> CheckAsync(.<string> metadata) { }
protected override string GetExpectation() { }
}
public class StringLengthValueAssertion : .<int>
{
public StringLengthValueAssertion(.<string> stringContext) { }
}
public class StringMatchesAssertion : .<..RegexMatchCollection>
{
public StringMatchesAssertion(.<string> context, . regex) { }
Expand Down Expand Up @@ -1578,7 +1582,10 @@ namespace .Extensions
public static .<TValue> EqualTo<TValue>(this .<TValue> source, TValue? expected, [.("expected")] string? expression = null) { }
public static ..HasFlagAssertion<TEnum> HasFlag<TEnum>(this .<TEnum> 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 .<string> source) { }
[("Use Length().IsEqualTo(expectedLength) instead.")]
public static . HasLength(this .<string> source, int expectedLength, [.("expectedLength")] string? expression = null) { }
public static .<TException> HasMessageContaining<TException>(this .<TException> source, string expectedSubstring, [.("expectedSubstring")] string? expression = null)
where TException : { }
Expand Down Expand Up @@ -1633,6 +1640,7 @@ namespace .Extensions
where TValue : <TValue> { }
public static .<TValue> IsPositive<TValue>(this .<TValue?> source)
where TValue : struct, <TValue> { }
public static . Length(this .<string> source) { }
[.(1)]
public static .<TObject> Member<TObject, TItem>(this .<TObject> source, .<<TObject, .<TItem>>> memberSelector, <.<.<TItem>, TItem>, .<.<TItem>>> assertions) { }
[.("Uses reflection for legacy compatibility. For AOT compatibility, use the Member<T" +
Expand Down Expand Up @@ -4350,7 +4358,10 @@ namespace .Sources
public .<TCollection, TItem> DoesNotContain(<TItem, bool> predicate, [.("predicate")] string? expression = null) { }
public .<TCollection, TItem> 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<TCollection, TItem> HasCount() { }
[("Use Count().IsEqualTo(expectedCount) instead.")]
public .<TCollection, TItem> HasCount(int expectedCount, [.("expectedCount")] string? expression = null) { }
public .<TCollection, TItem> HasDistinctItems() { }
public .<TCollection, TItem> HasSingleItem() { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1112,6 +1112,10 @@ namespace .Conditions
protected override .<.> CheckAsync(.<string> metadata) { }
protected override string GetExpectation() { }
}
public class StringLengthValueAssertion : .<int>
{
public StringLengthValueAssertion(.<string> stringContext) { }
}
public class StringMatchesAssertion : .<..RegexMatchCollection>
{
public StringMatchesAssertion(.<string> context, . regex) { }
Expand Down Expand Up @@ -1575,7 +1579,10 @@ namespace .Extensions
public static .<TValue> EqualTo<TValue>(this .<TValue> source, TValue? expected, [.("expected")] string? expression = null) { }
public static ..HasFlagAssertion<TEnum> HasFlag<TEnum>(this .<TEnum> 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 .<string> source) { }
[("Use Length().IsEqualTo(expectedLength) instead.")]
public static . HasLength(this .<string> source, int expectedLength, [.("expectedLength")] string? expression = null) { }
public static .<TException> HasMessageContaining<TException>(this .<TException> source, string expectedSubstring, [.("expectedSubstring")] string? expression = null)
where TException : { }
Expand Down Expand Up @@ -1630,6 +1637,7 @@ namespace .Extensions
where TValue : <TValue> { }
public static .<TValue> IsPositive<TValue>(this .<TValue?> source)
where TValue : struct, <TValue> { }
public static . Length(this .<string> source) { }
public static .<TObject> Member<TObject, TItem>(this .<TObject> source, .<<TObject, .<TItem>>> memberSelector, <.<.<TItem>, TItem>, .<.<TItem>>> assertions) { }
[.("Uses reflection for legacy compatibility. For AOT compatibility, use the Member<T" +
"Object, TItem, TTransformed> overload with strongly-typed assertions.")]
Expand Down Expand Up @@ -4330,7 +4338,10 @@ namespace .Sources
public .<TCollection, TItem> DoesNotContain(<TItem, bool> predicate, [.("predicate")] string? expression = null) { }
public .<TCollection, TItem> 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<TCollection, TItem> HasCount() { }
[("Use Count().IsEqualTo(expectedCount) instead.")]
public .<TCollection, TItem> HasCount(int expectedCount, [.("expectedCount")] string? expression = null) { }
public .<TCollection, TItem> HasDistinctItems() { }
public .<TCollection, TItem> HasSingleItem() { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1115,6 +1115,10 @@ namespace .Conditions
protected override .<.> CheckAsync(.<string> metadata) { }
protected override string GetExpectation() { }
}
public class StringLengthValueAssertion : .<int>
{
public StringLengthValueAssertion(.<string> stringContext) { }
}
public class StringMatchesAssertion : .<..RegexMatchCollection>
{
public StringMatchesAssertion(.<string> context, . regex) { }
Expand Down Expand Up @@ -1578,7 +1582,10 @@ namespace .Extensions
public static .<TValue> EqualTo<TValue>(this .<TValue> source, TValue? expected, [.("expected")] string? expression = null) { }
public static ..HasFlagAssertion<TEnum> HasFlag<TEnum>(this .<TEnum> 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 .<string> source) { }
[("Use Length().IsEqualTo(expectedLength) instead.")]
public static . HasLength(this .<string> source, int expectedLength, [.("expectedLength")] string? expression = null) { }
public static .<TException> HasMessageContaining<TException>(this .<TException> source, string expectedSubstring, [.("expectedSubstring")] string? expression = null)
where TException : { }
Expand Down Expand Up @@ -1633,6 +1640,7 @@ namespace .Extensions
where TValue : <TValue> { }
public static .<TValue> IsPositive<TValue>(this .<TValue?> source)
where TValue : struct, <TValue> { }
public static . Length(this .<string> source) { }
[.(1)]
public static .<TObject> Member<TObject, TItem>(this .<TObject> source, .<<TObject, .<TItem>>> memberSelector, <.<.<TItem>, TItem>, .<.<TItem>>> assertions) { }
[.("Uses reflection for legacy compatibility. For AOT compatibility, use the Member<T" +
Expand Down Expand Up @@ -4350,7 +4358,10 @@ namespace .Sources
public .<TCollection, TItem> DoesNotContain(<TItem, bool> predicate, [.("predicate")] string? expression = null) { }
public .<TCollection, TItem> 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<TCollection, TItem> HasCount() { }
[("Use Count().IsEqualTo(expectedCount) instead.")]
public .<TCollection, TItem> HasCount(int expectedCount, [.("expectedCount")] string? expression = null) { }
public .<TCollection, TItem> HasDistinctItems() { }
public .<TCollection, TItem> HasSingleItem() { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1044,6 +1044,10 @@ namespace .Conditions
protected override .<.> CheckAsync(.<string> metadata) { }
protected override string GetExpectation() { }
}
public class StringLengthValueAssertion : .<int>
{
public StringLengthValueAssertion(.<string> stringContext) { }
}
public class StringMatchesAssertion : .<..RegexMatchCollection>
{
public StringMatchesAssertion(.<string> context, . regex) { }
Expand Down Expand Up @@ -1476,7 +1480,10 @@ namespace .Extensions
public static .<TValue> EqualTo<TValue>(this .<TValue> source, TValue? expected, [.("expected")] string? expression = null) { }
public static ..HasFlagAssertion<TEnum> HasFlag<TEnum>(this .<TEnum> 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 .<string> source) { }
[("Use Length().IsEqualTo(expectedLength) instead.")]
public static . HasLength(this .<string> source, int expectedLength, [.("expectedLength")] string? expression = null) { }
public static .<TException> HasMessageContaining<TException>(this .<TException> source, string expectedSubstring, [.("expectedSubstring")] string? expression = null)
where TException : { }
Expand Down Expand Up @@ -1521,6 +1528,7 @@ namespace .Extensions
where TValue : <TValue> { }
public static .<TValue> IsPositive<TValue>(this .<TValue?> source)
where TValue : struct, <TValue> { }
public static . Length(this .<string> source) { }
public static .<TObject> Member<TObject, TItem>(this .<TObject> source, .<<TObject, .<TItem>>> memberSelector, <.<.<TItem>, TItem>, .<.<TItem>>> assertions) { }
public static .<TObject> Member<TObject, TItem>(this .<TObject> source, .<<TObject, .<TItem>>> memberSelector, <.<.<TItem>, TItem>, object> assertions) { }
public static .<TObject> Member<TObject, TMember>(this .<TObject> source, .<<TObject, TMember>> memberSelector, <.<TMember>, .<TMember>> assertions) { }
Expand Down Expand Up @@ -3819,7 +3827,10 @@ namespace .Sources
public .<TCollection, TItem> DoesNotContain(<TItem, bool> predicate, [.("predicate")] string? expression = null) { }
public .<TCollection, TItem> 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<TCollection, TItem> HasCount() { }
[("Use Count().IsEqualTo(expectedCount) instead.")]
public .<TCollection, TItem> HasCount(int expectedCount, [.("expectedCount")] string? expression = null) { }
public .<TCollection, TItem> HasDistinctItems() { }
public .<TCollection, TItem> HasSingleItem() { }
Expand Down
Loading
Loading