| Property | Value |
|---|---|
| Category | Reproducibility (JustDummies.Reproducibility) |
| Severity | 🟠 Warning |
| Enabled by default | Yes |
xUnit evaluates a theory’s data provider at discovery, before any test case runs. A value drawn there is therefore drawn once for the whole run, outside every seed scope — Any.Reproducibly and [Reproducible] alike.
Three defects ride on that one shape, and all three are silent:
- every case of the theory receives the same value, so a theory that reads as if it enumerated arbitrary cases enumerates a constant;
- the value is replayable from no reported seed, because no seed was pinned when it was drawn;
- the draw happens even for cases that are filtered out, and its position in the sequence shifts when cases are added or removed.
Draw in the test body, or let the provider yield the generator and materialize it inside the test.
Noncompliant
public static TheoryData<string> Cases => new() {
Any.String().NonEmpty().Generate(), // JD008: drawn at discovery, shared by every case
};
[Theory]
[MemberData(nameof(Cases))]
public void It_is_accepted(string reference) { /* ... */ }
Compliant
public static TheoryData<IAny<string>> Cases => new() {
Any.String().NonEmpty(), // the recipe travels, not the value
};
[Theory, Reproducible]
[MemberData(nameof(Cases))]
public void It_is_accepted(IAny<string> reference) {
string value = reference.Generate(); // drawn per case, inside the pinned scope
}
What it recognises as a provider
A member named by a [MemberData] in the same type; a member returning TheoryData or TheoryData<...>; a member returning a sequence of object[]; and a type implementing that sequence, which is the [ClassData] shape.
What it does not flag
- A provider that yields generators rather than values — the compliant shape above.
- A draw in an ordinary test body, which is the point of the rule.
- A draw from an isolated
Any.WithSeed(...)context, or a generator reached through a local or field rather than written inline fromAny.