Define the observable contract
Generated cases can explore a broader input set, but reproducibility has a scope. A seeded Random instance lets this program repeat a sequence within the same implementation and configuration. It does not make the exact sequence a permanent cross-version interchange format. Record an actual failing input as well as its seed when preserving a defect. The lesson deliberately asserts repeatability and bounds without printing specific random values, so its expected output does not depend on choosing one desktop implementation's eight numbers as the browser's universal reference data.
Walk through the real program
Draw creates a new Random with the supplied seed and requests eight integers from zero inclusive to ten exclusive. Calling Draw twice with forty-two creates two independently initialized generators under the same runtime. Their produced arrays compare equal, printing same:True. Every generated value satisfies the documented range, so the second line is range:True. The array length is eight, producing cases:8. Those booleans are manually predictable from the API contract; the example does not claim that eight generated inputs exhaust the possible values or prove a separate algorithm correct.
Why the design is testable
Random generation is a tool for selecting inputs, not an oracle that tells the test what the answer should be. Once a generated case reaches a domain function, its expected behavior needs a stated example, invariant, reference implementation, or justified relation. Keep generation bounded and describe its domain. A fixed seed aids debugging, while a saved minimal counterexample makes a regression durable even if the generator changes. If several operations consume one shared generator, an added draw can shift all subsequent inputs and make a seed alone harder to interpret.
A boundary that matters
Do not use Random.Shared for a test that requires one private reproducible sequence; other callers can consume unrelated values. Likewise, do not assert that a random sample necessarily contains every category, a duplicate, or a boundary unless the construction guarantees it. Such probabilistic expectations create flaky checks. System.Random is also not a cryptographic source, and deterministic seeds must not be repurposed as security tokens. This course uses it only for bounded input selection and keeps its version limits separate from the underlying domain behavior being investigated.
Practice: isolate one behavior
First change the count of generated values from eight to twelve in both draws and predict the same two true booleans with cases:12. Hint: update the declared workload size, not a guessed list of values. Then request the half-open range five through ten and verify the corresponding bound property. Add explicit cases for the domain's important endpoints independently of random sampling, because a short sequence may never choose them. Explain which facts come from construction and which still require a test expectation.
Practice: challenge another assumption
For a second exercise, pass each generated integer into a simple domain rule and check a justified property. When a deliberately broken implementation fails, print and retain the particular input before minimizing it. Hint: a small concrete regression should stand on its own without rerunning the whole generator. Repeat under another documented runtime if available and compare the observations without requiring identical numeric sequences across versions. Keep framework-level randomized or property-based testing tools separate from this portable demonstration, which implements neither their generation features nor their shrinking algorithms.
Review the evidence
The unchanged output documents same-seed repeatability, the accepted Next range, and the bounded sample size. It does not turn randomness into proof or guarantee a cross-runtime number stream. A strong generated-case workflow combines controlled generation, independent behavioral expectations, useful failure reporting, and retained counterexamples. That combination makes exploration reproducible enough to investigate while keeping permanent regression evidence understandable. Choose the generator according to the input domain and be explicit when a property depends on construction rather than on chance. When preserving a generated failure, record the bounds, draw count and consumption order as well as the seed; an added earlier draw can shift later cases even without a runtime upgrade.
- The code asserts repeatability and bounds, not particular generated numbers.
- The upper bound passed to Next is exclusive.
- Preserve the failing input in addition to its seed and runtime context.
A working example
using System;
using System.Linq;
using System.Collections.Generic;
using System.Globalization;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
int[] Draw(int seed)
{
var random = new Random(seed);
return Enumerable.Range(0, 8).Select(_ => random.Next(0, 10)).ToArray();
}
int[] first = Draw(42);
int[] second = Draw(42);
Console.WriteLine($"same:{first.SequenceEqual(second)}");
Console.WriteLine($"range:{first.All(value => value >= 0 && value < 10)}");
Console.WriteLine($"cases:{first.Length}");
Expected output
same:True range:True cases:8
A seed helps within a known implementation; record the actual failing input too.