| Property | Value |
|---|---|
| Category | Usage (JustDummies.Usage) |
| Severity | 🟠 Warning |
| Enabled by default | No — opt-in |
Generators are reference types, so an object, dynamic or params object[] position accepts one with no conversion at all. This is the residue ADR-0006 opens in a new tab could not close by removing the implicit conversions: there was nothing to remove here.
The recipe then survives as an opaque object:
- an assertion helper taking
objectinspects the recipe —Assert.NotNull(Any.String())is green for ever and asserts nothing; - a theory row built as
object[]feeds the generator itself to the code under test; gen.Equals(value)resolves toobject.Equals, which is reference equality against an unrelated object: false for every run and every seed.
Enabling it
dotnet_diagnostic.JD011.severity = warning
Noncompliant
Assert.NotNull(Any.String().NonEmpty()); // JD011: asserts the recipe is non-null. It always is.
object[] row = { Any.Int32().Positive(), 1 }; // JD011: the row carries a recipe
bool same = Any.String().NonEmpty().Equals(expected); // JD011: false, always
Compliant
Assert.NotNull(Any.String().NonEmpty().Generate());
object[] row = { Any.Int32().Positive().Generate(), 1 };
bool same = Any.String().NonEmpty().Generate().Equals(expected);
Why it ships opt-in
ADR-0038 opens in a new tab required this rule’s default to be decided on measurement rather than intuition. Dogfooded across this repository’s suites it produced no true positive and two false ones, both in a convention test that collects generators into a List<object> on purpose — a shape indistinguishable from the theory-row mistake the rule exists to catch, and therefore impossible to narrow away.
That is weak evidence for enabling it everywhere, and good evidence that it belongs in a consumer’s suite, where object-typed assertion helpers are common and reflection over generator instances is not. Enable it there.
What it does not flag
- A comparison between two generators —
ReferenceEquals(original, narrowed)andfirst.Equals(second). Comparing recipes by identity is how an immutability test proves a constraint returned a new generator;Generate()there would destroy the property under test. Assert.Throws<T>(() => Any.String().WithLength(3)), which binds toFunc<object>rather thanActionand so produces a real generator-to-objectconversion. That shape exists at 88+ sites in this repository alone.- A generated value, which is the point.