Unit Testing with functions that return random results
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Functions that return random results are only hard to test when the randomness is mixed directly into the logic you care about. The standard fix is to control the random source during tests and then assert deterministic outcomes or invariant properties. Once randomness becomes an input, the function becomes testable again.
Inject the Random Source
A clean design takes a random generator or callback as a dependency.
In production, the function still behaves randomly. In tests, you can pass a seeded generator.
The test is deterministic because the random source is deterministic.
Use Fakes for Exact Branch Control
Sometimes a seed is still more indirect than you want. A fake random object makes the test intention explicit.
This is especially helpful when the production code calls several random methods and you want total control over the path.
Test Invariants Instead of Exact Outcomes
Some properties should be true regardless of the random value. Those are often better unit tests than one exact seeded output.
This checks a real contract: the function never invents a result outside the input set.
Separate Randomness From Business Logic
If a function both generates random numbers and applies complex business rules, split those concerns.
Now choose_by_index has no randomness at all, so the core business behavior can be tested with ordinary deterministic unit tests.
Statistical Tests Are a Different Category
Sometimes you do want to verify approximate fairness or distribution. That is a different kind of test and should usually live outside the smallest unit-test layer.
Keep thresholds broad if you do this. Statistical assertions are inherently noisier than ordinary unit tests.
A Small Dice Example
This test verifies the real guarantee without pretending the same face must always appear in production.
Common Pitfalls
- Calling the global random generator directly inside business logic and then writing brittle tests against exact outputs.
- Using a seed when a fake RNG would express the test intention more clearly.
- Treating a fairness check as though it were a perfectly deterministic unit test.
- Asserting too little, such as only the return type, instead of the real contract.
- Leaving randomness entangled with business logic so the important rules remain hard to test.
Summary
- Inject randomness so tests can control it.
- Use seeded generators or fake RNG objects for deterministic test cases.
- Assert invariants such as valid ranges and membership, not only exact outputs.
- Separate random-number generation from business logic when possible.
- Treat fairness checks as statistical tests, not as ordinary unit tests.

