JustDummies 1.0.0-preview.1

This is a prerelease version of JustDummies.
dotnet add package JustDummies --version 1.0.0-preview.1
                    
NuGet\Install-Package JustDummies -Version 1.0.0-preview.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="JustDummies" Version="1.0.0-preview.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="JustDummies" Version="1.0.0-preview.1" />
                    
Directory.Packages.props
<PackageReference Include="JustDummies" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add JustDummies --version 1.0.0-preview.1
                    
#r "nuget: JustDummies, 1.0.0-preview.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package JustDummies@1.0.0-preview.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=JustDummies&version=1.0.0-preview.1&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=JustDummies&version=1.0.0-preview.1&prerelease
                    
Install as a Cake Tool

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 with AllowingCombinations()), TimeSpan, DateTime (UTC) and DateTimeOffset. On modern targets (net8.0) the surface extends to DateOnly, TimeOnly, Int128, UInt128 and Half; the package also targets netstandard2.0 and 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 yields existing.
  • Custom alphabets: Any.String().WithChars("αβγδε") draws the string from an explicit character pool — the general form of the built-in Alpha/Numeric/ AlphaNumeric sets, and the way to reach non-ASCII text (accents, Greek, Cyrillic, CJK) without a StringMatching literal. 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 with OneOf("😀", "🎉") 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's OneOf: the other constraints narrow the set rather than shape a string, so Any.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, and Any.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-rolled pool[new Random().Next(...)] that would ignore the seed and break Reproducibly. Uniform like the string set: duplicates collapse under the default comparer, the pool's distinct count gates distinct collections, and a null element 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, and Any.ElementOf(orders).DifferentFrom(theOneAlreadyUsed) is the idiom for drawing another element of a fixture.
  • URIs by family: Any.Uri() yields an arbitrary yet valid System.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 no WithPort, WebSocket() no WithUserInfo): 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 the file scheme stay out of the default draw to keep that determinism.
  • Domain vocabulary where it belongs: dates constrain with After/Before/Between, quantities with Positive/Between/NonZero, identities with NonEmpty/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 MultipleOfAny.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 an As(x => x * 100) projection that silently distorts it. Decimal takes WithScale(n), a value expressible in n decimal places (WithScale(2) for a currency amount) — a value lattice (a multiple of 10⁻ⁿ), not a padded representation. The temporal generators take WithGranularity(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 offset TimeSpan.Zero (UTC); WithOffset(TimeSpan) pins a whole-minute offset (±14:00) and WithOffsetBetween(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 with OneOf(...), 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-bearing AnyGenerationException, 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 ConflictingAnyConstraintException at the moment the conflicting constraint is declared — for example Any.String().WithLength(3).StartingWith("ORD-").
  • Dummies stay ordinary unless you ask for more. An unconstrained Any.Double(), Single() or Decimal() 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, while Between(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 large int is an ordinary int.
  • Dummies stay small unless you ask for more. A bound is a permission, not a request: WithMaxLength/WithMaxCount only ever narrow a draw, so Any.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/WithMinCount raise an ArgumentOutOfRangeException naming 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, SetOf and DictionaryOf, constrained with WithCount/NonEmpty/Distinct/Containing. Ask a distinct collection for more distinct elements than its effective domain — the element generator plus any values pinned outside it with Containing — 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 an AnyGenerationException naming the seed to replay. Any.PairOf/TripleOf pair generators into value tuples.
  • Optional values: .OrNull() turns any generator into one that is null about 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 an Any.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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .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