JustDummies 1.0.0-preview.1
dotnet add package JustDummies --version 1.0.0-preview.1
NuGet\Install-Package JustDummies -Version 1.0.0-preview.1
<PackageReference Include="JustDummies" Version="1.0.0-preview.1" />
<PackageVersion Include="JustDummies" Version="1.0.0-preview.1" />
<PackageReference Include="JustDummies" />
paket add JustDummies --version 1.0.0-preview.1
#r "nuget: JustDummies, 1.0.0-preview.1"
#:package JustDummies@1.0.0-preview.1
#addin nuget:?package=JustDummies&version=1.0.0-preview.1&prerelease
#tool nuget:?package=JustDummies&version=1.0.0-preview.1&prerelease
JustDummies
A fluent DSL for generating arbitrary yet valid test values — dummies: values a test needs but never asserts on.
Website: justdummies.io
The idea
A test's Arrange is full of values the test does not check: an order reference, a
quantity, a label. A hand-picked literal reads as significant even when it is not.
JustDummies makes the incidental legible as incidental — and, when the value must cross
an invariant (a value object, a contract precondition), the constraints express that
invariant, never what the test asserts:
string code = Any.String()
.NonEmpty()
.WithMaxLength(50)
.StartingWith("ORD-")
.Generate();
Read it as: any string that satisfies these constraints. The exact value does not matter — and that is the point.
What's inside
- Fluent, typed generators implementing
IAny<T>, materialized through.Generate(), across the .NET simple types:String,Char, every integer width (SByte/Byte/Int16/UInt16/Int32/UInt32/Int64/UInt64),Double/Single/Decimal(finite values only — never NaN or infinities),Boolean,Guid,Enum<T>(declared members only — a[Flags]enum widens to every combination withAllowingCombinations()),TimeSpan,DateTime(UTC) andDateTimeOffset. On modern targets (net8.0) the surface extends toDateOnly,TimeOnly,Int128,UInt128andHalf; the package also targetsnetstandard2.0and runs on .NET Framework 4.7.2+, .NET Core 2.0+ and .NET 5+ for the widest reach — with the .NET Framework 4.7.2 floor exercised in CI, not merely advertised. - Strings from a regex:
Any.StringMatching(pattern)generates arbitrary strings that match a regular expression — the dummy for a format-validated value object. Home-grown (zero dependencies) over the regular subset of the pattern language; a non-regular construct (a lookaround, a backreference) is refused with a clear error rather than a silently non-matching value. The pattern is the whole shape — express a length or a prefix inside it, since building a value in the intersection of two regular languages is not something the library does — but the exclusion pair is there:Any.StringMatching(@"^ORD-\d{8}$").DifferentFrom(existing)never yieldsexisting. - Custom alphabets:
Any.String().WithChars("αβγδε")draws the string from an explicit character pool — the general form of the built-inAlpha/Numeric/AlphaNumericsets, and the way to reach non-ASCII text (accents, Greek, Cyrillic, CJK) without aStringMatchingliteral. It stays within the Basic Multilingual Plane and rejects a surrogate: an emoji or other astral character is an atomic grapheme, not a character family, so draw those as whole strings withOneOf("😀", "🎉")instead. Anchored fragments must be drawn from the pool, or the conflict is reported at declaration. - Strings from an explicit set:
Any.String().OneOf("EUR", "USD", "GBP")draws from a fixed, closed list — the dummy for a value whose domain is a short enumeration (a currency code, a well-known name). Composable like every other family'sOneOf: the other constraints narrow the set rather than shape a string, soAny.String().OneOf("abc", "de").WithLength(3)yields"abc"and a constraint no supplied value satisfies is a conflict naming both sides, whichever order the two were declared in. Duplicates collapse, and the draw is uniform and reproducible under a seed. - Any value from an explicit pool:
Any.OneOf(eur, usd, gbp)draws one value from a caller-supplied set of arbitrary values or domain objects, andAny.ElementOf(orders)does the same from a collection already held (a list, a LINQ result). This is the seed-aware answer to "any of these" — replacing a hand-rolledpool[new Random().Next(...)]that would ignore the seed and breakReproducibly. Uniform like the string set: duplicates collapse under the default comparer, the pool's distinct count gates distinct collections, and anullelement is refused — make the whole draw optional with.OrNull()instead. The element type is opaque to the library, so the pool is the whole shape of the specification; what it does offer is the exclusion pair, andAny.ElementOf(orders).DifferentFrom(theOneAlreadyUsed)is the idiom for drawing another element of a fixture. - URIs by family:
Any.Uri()yields an arbitrary yet validSystem.Uri— an absolute web (http/https), WebSocket (ws/wss), FTP or mailto URI, or a relative reference. Narrow it to a family and each returns a builder exposing only that family's valid components, so an impossible combination cannot even be written (Mailto()has noWithPort,WebSocket()noWithUserInfo):Any.Uri().Web().UsingHttps().WithHost("api.example.com"). Every part is drawn from ASCII-unreserved characters, so a value is valid by construction and reproducible across frameworks; internationalized (IDN) hosts and thefilescheme stay out of the default draw to keep that determinism. - Domain vocabulary where it belongs: dates constrain with
After/Before/Between, quantities withPositive/Between/NonZero, identities withNonEmpty/DifferentFrom— and deliberately no clock-relative constraints: a reproducible test pins its reference instants explicitly. - Values on a grid: a quantity that must be a whole number of some unit takes
MultipleOf—Any.Int32().Between(0, 100_000).MultipleOf(100)for an amount in whole euros held as cents — drawn on the grid so the declared range keeps its meaning, instead of anAs(x => x * 100)projection that silently distorts it.DecimaltakesWithScale(n), a value expressible inndecimal places (WithScale(2)for a currency amount) — a value lattice (a multiple of10⁻ⁿ), not a padded representation. The temporal generators takeWithGranularity(TimeSpan)— a round instant or duration (WithGranularity(TimeSpan.FromMinutes(15))) — so tick-precision values never surprise a serialization round-trip. Each is built in one draw, composes with the bounds and exclusions, and conflicts eagerly when the range holds no grid point. - Offset-aware
DateTimeOffset: unconstrained,Any.DateTimeOffset()carries offsetTimeSpan.Zero(UTC);WithOffset(TimeSpan)pins a whole-minute offset (±14:00) andWithOffsetBetween(min, max)draws a bounded one, so offset-sensitive code (local rendering, offset arithmetic, "same instant, different offset") is actually exercised. The instant is tightened first, so the value stays valid even at the edges of the range. Combined withOneOf(...), the declared offset selects which pooled values may be drawn — pooled values keep their own offset rather than being rewritten — and an offset none of them carries is a conflict, whichever of the two is declared first. - Values built to satisfy the constraints — a scalar is constructed directly,
never generated-then-filtered. The one exception is excluding values from a string or a
pattern (
Any.String().DifferentFrom(...)/Except(...),Any.StringMatching(p).DifferentFrom(...)): neither has an ordinal mapping to build the exclusion into, so it is met by a bounded redraw — the same escape a distinct collection uses to skip a duplicate, never an unbounded retry loop. An exclusion tight enough to leave nothing surfaces at generation as a seed-bearingAnyGenerationException, whose message reports the budget it spent rather than claiming no value remains — the search is bounded, so it never established that. An exclusion on an explicit value set needs no redraw at all: the domain is finite, so the values are removed at declaration and emptying it is a conflict there. - Conflicting constraints fail fast with a clear, actionable
ConflictingAnyConstraintExceptionat the moment the conflicting constraint is declared — for exampleAny.String().WithLength(3).StartingWith("ORD-"). - Dummies stay ordinary unless you ask for more. An unconstrained
Any.Double(),Single()orDecimal()draws within a magnitude of a million, not across the type's whole domain — so arithmetic on a dummy stays finite,WithScale(2)still has decimal places to constrain, and the value sits where rounding and formatting defects actually live. The window only ever clips:Between(0, double.MaxValue)permits a huge value and still yields an ordinary one, whileBetween(1e300, 1e308)names a magnitude and gets exactly it.Half, whose domain stops at 65 504, is unaffected. The integer generators deliberately keep their full range — a largeintis an ordinaryint. - Dummies stay small unless you ask for more. A bound is a permission, not a
request:
WithMaxLength/WithMaxCountonly ever narrow a draw, soAny.String().WithMaxLength(100_000)still yields the short unconstrained string rather than one sized after the cap. Only a minimum, an exact size or a required fragment enlarges a value —WithMinLength(90_000)is how you ask for a large one.WithLengthBetween(a, b)is exactly its two bounds declared separately, so a range starting at zero reads as a limit, not as a request to spread across it. A size the generator must actually produce is capped at 1 000 000: past that,WithLength/WithMinLength/WithCount/WithMinCountraise anArgumentOutOfRangeExceptionnaming your own parameter, instead of hanging or exhausting memory. A pure maximum is never capped — mirror a four-million-character column limit if you like; it costs nothing to honour. - Composition without reflection:
.As(factory)turns a constrained primitive into a domain value object;Any.Combine(...)assembles larger objects through constructor lambdas — from two up to eight constrained parts. - Collections over any element generator:
Any.ListOf(item),ArrayOf,SequenceOf,SetOfandDictionaryOf, constrained withWithCount/NonEmpty/Distinct/Containing. Ask a distinct collection for more distinct elements than its effective domain — the element generator plus any values pinned outside it withContaining— can supply, and it fails fast, just like any other conflict, wherever that domain is countable; where it is not, the same shortfall instead surfaces at generation as anAnyGenerationExceptionnaming the seed to replay.Any.PairOf/TripleOfpair generators into value tuples. - Optional values:
.OrNull()turns any generator into one that isnullabout half the time and otherwise a constrained value — the dummy for an optional field, for value types (int?,Guid?, ...) and reference types alike. - Reproducible runs: wrap a test in
Any.Reproducibly(...)and a failing run reports the seed to replay;Any.WithSeed(seed)gives an isolated, deterministic context;Any.UseSeed(seed)pins the ambient one until the handle is disposed, for a caller that has no body to wrap — a test-framework adapter driving the seed from before/after hooks. Its second overload names what the reader must write to replay, so a run pinned from outside the test body never points at a call the test does not contain. Drawing from several threads at once is safe — values stay arbitrary and well-formed — but concurrent draws interleave, so a seed replays a run only while its draws are taken one at a time; open anAny.UseSeed(...)scope per unit of work to keep a parallel run reproducible. A seed keeps its meaning across upgrades: within a major version, it draws the same values in every patch and minor, so a pinned seed committed today still covers the case it was pinned for after an upgrade. The mapping may change on a major version.
Example
using JustDummies;
OrderReference reference = Any.String()
.StartingWith("ORD-")
.WithLength(12)
.As(OrderReference.Create)
.Generate();
What it is not
No realistic fake data (names, emails, addresses), no object-graph auto-filling, no reflection. Small, deterministic, explicit.
And not a source of security material. Every draw comes from a seeded System.Random,
because a dummy is only worth generating if the seed a failing run reports replays it —
the very property that makes the sequence predictable to anyone who learns the seed.
Never draw a password, token, key, salt, nonce, or any identifier that has to be
unguessable from Any.*; reach for
System.Security.Cryptography.RandomNumberGenerator for those.
Documentation
Full documentation on GitHub:
https://github.com/Reefact/just-dummies
Credits
The package icon is a crash-test dummy by Magnific, from Flaticon.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- No dependencies.
-
net8.0
- No dependencies.
NuGet packages (1)
Showing the top 1 NuGet packages that depend on JustDummies:
| Package | Downloads |
|---|---|
|
JustDummies.Xunit
The xUnit v3 companion of JustDummies: mark a test, a class or an assembly [Reproducible] and its arbitrary values are drawn from a pinned seed, reported only when the test fails. Removes the per-test Any.Reproducibly ceremony without changing how values are generated. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0-preview.1 | 544 | 8/7/2026 |
| 0.1.0-preview.1 | 140 | 7/31/2026 |