Skip to content

feat: add string length range assertions#4935

Open
thomhurst wants to merge 3 commits intomainfrom
feat/string-length-assertions
Open

feat: add string length range assertions#4935
thomhurst wants to merge 3 commits intomainfrom
feat/string-length-assertions

Conversation

@thomhurst
Copy link
Owner

Summary

  • Adds HasMinLength(int minLength) assertion that verifies a string's length is >= the specified minimum
  • Adds HasMaxLength(int maxLength) assertion that verifies a string's length is <= the specified maximum
  • Adds HasLengthBetween(int min, int max) assertion that verifies a string's length is within an inclusive range

All three follow the existing assertion patterns (condition classes in StringAssertions.cs, extension methods in AssertionExtensions.cs) and support CallerArgumentExpression for readable error messages.

Closes #4868

Test plan

  • Verify HasMinLength passes for strings at or above the minimum length
  • Verify HasMinLength fails for strings below the minimum length
  • Verify HasMaxLength passes for strings at or below the maximum length
  • Verify HasMaxLength fails for strings above the maximum length
  • Verify HasLengthBetween passes for strings within the inclusive range
  • Verify HasLengthBetween fails for strings outside the range
  • Verify all three handle null strings with appropriate error messages
  • Verify And/Or chaining works (e.g., Assert.That(str).HasMinLength(3).And.HasMaxLength(10))

@claude
Copy link
Contributor

claude bot commented Feb 19, 2026

Code Review

The implementation follows the established patterns in StringAssertions.cs correctly — constructor, CheckAsync, GetExpectation, and the CallerArgumentExpression extension methods all match the existing convention for parameterized string assertions. The null/exception guard ordering is consistent with the rest of the file.

Two issues found:


1. Missing Public API snapshot updates (CLAUDE.md violation)

Severity: Required

TUnit.PublicAPI contains snapshot tests that verify the entire public surface area of the TUnit.Assertions assembly. The PR adds six new public members, but none of the four .verified.txt files were updated:

  • TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet8_0.verified.txt
  • TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet9_0.verified.txt
  • TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet10_0.verified.txt
  • TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.Net4_7.verified.txt

The analogous existing type StringLengthAssertion and its HasLength extension method are already captured in those snapshots. Without updating these files, the Public API tests will fail. Per CLAUDE.md Critical Rule 2:

"Changes to source generator output or public APIs require running snapshot tests. Commit .verified.txt files. NEVER commit .received.txt."

Fix: Run the TUnit.PublicAPI test suite and accept the generated .received.txt snapshots:

# From the TUnit.PublicAPI directory
dotnet test
# Then accept the output:
for f in *.received.txt; do mv "$f" "${f%.received.txt}.verified.txt"; done
git add *.verified.txt

2. HasLengthBetween silently produces an unsatisfiable assertion when minLength > maxLength

Severity: Design concern

When arguments are accidentally swapped — e.g., HasLengthBetween(10, 3) — the condition value.Length >= 10 && value.Length <= 3 is impossible to satisfy, so the assertion will silently always fail. The failure message will read something like "found length 5", giving the developer no indication that the real problem is the inverted bounds.

This is a genuine usability trap: the arguments have the same type (int, int), swapping them is easy, and the resulting failure message is actively misleading.

Every comparable assertion library (FluentAssertions, Shouldly, NUnit) throws ArgumentOutOfRangeException eagerly at construction time rather than letting the assertion silently become unsatisfiable.

Suggested fix — add a guard in HasLengthBetween (the extension method is the right place since it's the public entry point):

public static StringLengthBetweenAssertion HasLengthBetween(
    this IAssertionSource<string> source,
    int minLength,
    int maxLength,
    [CallerArgumentExpression(nameof(minLength))] string? minExpression = null,
    [CallerArgumentExpression(nameof(maxLength))] string? maxExpression = null)
{
    if (minLength > maxLength)
        throw new ArgumentOutOfRangeException(nameof(minLength),
            $"minLength ({minLength}) must be less than or equal to maxLength ({maxLength}).");
    source.Context.ExpressionBuilder.Append($".HasLengthBetween({minExpression}, {maxExpression})");
    return new StringLengthBetweenAssertion(source.Context, minLength, maxLength);
}

Reviewed by Claude — checked for bugs and CLAUDE.md compliance.

@thomhurst thomhurst force-pushed the feat/string-length-assertions branch from b950983 to 28ce284 Compare February 19, 2026 08:05
@thomhurst thomhurst force-pushed the feat/string-length-assertions branch from 28ce284 to e23c286 Compare February 19, 2026 09:38
@thomhurst thomhurst force-pushed the feat/string-length-assertions branch from e23c286 to cf5f7b1 Compare February 19, 2026 11:46
@thomhurst thomhurst force-pushed the feat/string-length-assertions branch from cf5f7b1 to c10dc39 Compare February 19, 2026 14:06
… HasLengthBetween)

Add three new string assertion methods for validating string length ranges:
- HasMinLength(int) - asserts string length >= minLength
- HasMaxLength(int) - asserts string length <= maxLength
- HasLengthBetween(int, int) - asserts min <= string length <= max

Closes #4868
Add verified snapshots for HasMinLength, HasMaxLength, and HasLengthBetween
assertion methods added to the string assertions API.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: add string length range assertions (HasMinLength, HasMaxLength, HasLengthBetween)

1 participant