Skip to main content

Deterministic randomness

For snapshot tests, generated-id assertions, or reproducing a reported bug, you want the "random" values to be predictable. RandomProvider.Generate(...) lets you supply a generator for any of the primitive types Random produces:

IRandomProvider provider = RandomProvider.Generate(
seed: 42,
intGenerator: new[] { 1, 2, 3 }, // round-robins through the array
guidGenerator: () => Guid.Parse("00000000-0000-0000-0000-000000000001"),
doubleGenerator: 0.5); // always returns 0.5

MockRandomSystem randomSystem = new(provider);

randomSystem.Random.Shared.Next(); // 1
randomSystem.Random.Shared.Next(); // 2
randomSystem.Random.Shared.Next(); // 3
randomSystem.Random.Shared.Next(); // 1 (wraps around)
randomSystem.Guid.NewGuid(); // 00000000-0000-0000-0000-000000000001

Generate accepts generators for int, long, float, double, byte[] and Guid. Any generator left as null falls through to the seeded Random.

Building generators

Generator<T> has implicit conversions from T, T[] and Func<T>, plus four explicit factory methods so most call sites stay terse:

// From a single value (every call returns 42) - implicit, or explicit Generator.FromValue(42)
Generator<int> g1 = 42;

// From an array (round-robin) - implicit, or explicit Generator.FromArray(new[] { 1, 2, 3 })
Generator<int> g2 = new[] { 1, 2, 3 };

// From a callback - implicit from Func<T>, or explicit Generator.FromCallback(...)
Generator<int> g3 = Generator.FromCallback(() => DateTime.Now.Millisecond);

// From any IEnumerable (loops once exhausted)
Generator<Guid> g4 = Generator.FromEnumerable(MyKnownGuids);

If you have a Generator<T> directly, g.GetNext() returns the next value - useful when you build the generator yourself outside a MockRandomSystem (e.g. for parameterised test-data generation).

Generator<T> is IDisposable - when wrapping an IEnumerable<T>, dispose it (or rely on garbage collection in tests) to release the underlying enumerator.

Wiring it into the file system

MockFileSystem uses the random system internally for things like Path.GetRandomFileName() and WithAFile(). Pass a deterministic provider through to keep file-system tests reproducible:

MockFileSystem fileSystem = new(o =>
o.UseRandomProvider(RandomProvider.Generate(seed: 1234)));