| Property | Value |
|---|---|
| Category | Constraints (JustDummies.Constraints) |
| Severity | 🔵 Info |
| Enabled by default | Yes |
An Any.String() chain declares no length, so it draws the whole default spread: 0 to 1024 characters, from anywhere in ASCII.
That default is deliberately inconvenient (ADR-0076 opens in a new tab). A dummy short enough to be comfortable is one no length invariant is ever exercised against: the test passes, and it has established nothing about what your code does at three hundred characters. But an inconvenient default only teaches when something names the remedy — and a wall of characters in a failure message does not say WithMaxLength. This rule says it, at the call site, which is the one place you can act on it.
Noncompliant
string label = Any.String().Generate();
string name = Any.String().NonEmpty().Generate();
NonEmpty() does not settle a length: it raises the floor to one and leaves the ceiling where it was, so the draw still spans the whole spread — 1 to 1025 rather than 0 to 1024. The message reports the interval the chain actually draws, not a constant.
Anchors move it the same way, because a draw can never be shorter than the literals it must contain: StartingWith("hello") reports 5 to 1029, and NotBlank() adds one position more where no anchor already carries a non-blank character, so StartingWith(" ").NotBlank() reports 2 to 1026 while StartingWith("A").NotBlank() reports 1 to 1025. An anchor the compiler cannot fold to a constant is left out of that sum, which understates the floor rather than overstating it.
Compliant
string label = Any.String().WithMaxLength(50).Generate();
string code = Any.String().WithLength(8).Generate();
string name = Any.String().NonEmpty().WithMaxLength(255).Generate();
string range = Any.String().WithLengthBetween(3, 20).Generate();
string set = Any.String().OneOf("EUR", "USD", "GBP").Generate();
Declare the length the surrounding code actually allows — a column width, a contract bound, an exact size. That is the same contract the rest of the library applies: a constraint expresses what the code requires of the value, never what the test asserts.
Scope
The rule reasons about one written expression, rooted at an Any.String() factory call — it does not follow a generator through a local, a field or a helper. A factory call written with no length is reported the moment it is written, even when a later statement reassigns the variable to a narrower generator:
AnyString label = Any.String(); // reported here: this expression alone declares no length
label = label.WithMaxLength(5); // a new, narrower generator — the report already happened
The message names the interval that expression would draw if generated as written, not necessarily what the variable ends up holding two lines later. Bind the length in the same expression that starts the chain to avoid it.
What it does not flag
- A value set.
OneOf(...)supplies the values, so their lengths are yours and no spread applies. - A pattern.
Any.StringMatching(...)carries its shape inside the pattern. - Any generator other than
Any.String().
Why it is information, not a warning
A length a test genuinely does not care about is a legitimate thing to leave unsaid: a value passed to a logger, a field nothing downstream measures. The rule states a fact to weigh, never a verdict — the same posture as JD029. Where a call site genuinely does not care, silence it there with a [SuppressMessage] naming JustDummiesRule.JD030 — see JustDummies.DiagnosticCatalog.