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(catalogNumbers);

If you have a Generator<T> directly, g.GetNext() returns the next value, which is useful when you build the generator yourself outside a MockRandomSystem (e.g. for parameterized 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)));