Ask what the test reads from outside itself
A test that passes for weeks and then fails on the first of the month, after a clock change or only on a colleague's laptop is reading something it does not control. Make a list of everything the test and the code it calls read from outside: the current date and time, the time zone, random values, environment variables, the locale, the working directory, a file's modification time, the order of items returned by a dictionary or a database, and the versions of installed packages. Then compare a failing run with a passing one on those points: when did each run, on which machine, with which settings?
- Check failure timestamps against month ends, year ends, midnight and daylight-saving changes.
- Check whether failures cluster on one runner, one operating system or one Python version.
- Check whether the failing assertion compares two values that were generated separately.
Control the input instead of waiting for the failure
Martin Fowler's advice for time is to wrap the system clock so tests can freeze or seed it, and to substitute the wrapper in tests. For randomness, a randomiser stubbed to a constant value can expose collision bugs that only appear when two generated values happen to match. The pytest documentation also suggests that, for code you control, passing a dependency in explicitly is better than patching it globally. A clock or a random source passed into a function is easy to replace and needs no patching at all. Once the input is fixed, replay the failing conditions deliberately: set the clock to the failing date and the test either fails every time, which is a defect you can now see, or it does not, which means you have not found the cause.
- Write a test for the awkward dates, such as the last day of a month and a daylight-saving boundary, with the clock fixed.
- Seed or stub random values in tests so two runs generate the same data.
- Set environment variables in the test through the test framework, so they are restored afterwards.
Patch the name your code looks up
When you do patch, the Python documentation for unittest.mock states the rule: patch where an object is looked up, not where it is defined. If one module imports a class with from a import SomeClass, patching it in module a has no effect on that module, because it already holds a reference to the real one. The pytest documentation adds two cautions: patch the name your own module uses, and avoid patching standard-library functions or builtins broadly, because that can break pytest itself. If a standard-library patch cannot be avoided, wrap it in a narrow monkeypatch.context() block. A patch started by hand must be stopped by hand; decorators and with blocks undo themselves.
- A patch that appears to do nothing usually targets the wrong module path.
- Keep a patch as narrow as the lines that need it, so it cannot leak into later tests.
What does not fit, and how a paid fix is accepted
If fixing the clock reveals that production code mishandles a date, that is a defect, and the repair route is one bug with a failing regression test. If a test fails because an earlier test changed shared state, the state-leak guide applies instead. For a named set of tests that fail at random, the one-off stabilisation job reproduces each failure by repeated runs, classifies its cause with the evidence, and then fixes the cause or quarantines the test openly. Acceptance is the baseline failure shown first, then the agreed number of consecutive clean runs under the same conditions, with no assertion loosened and no wait lengthened as the fix. We state the clean count and its limits; it does not prove a flake is gone. Start an enquiry with test names and redacted messages only.
Sources and limits
- Martin Fowler: Eradicating Non-Determinism in Tests Checked 2026-10-11.
- Time is a named cause: wrap the system clock so tests can freeze or seed it.
- Stubbing a randomiser to a constant can expose collision bugs, and a resource pool of size one can make a leaking test fail itself.
- Python documentation: unittest.mock, where to patch (Python 3.15.0 documentation) Checked 2026-10-11.
- patch changes which object a name points to, so you patch the name where it is looked up, which is not necessarily where it is defined.
- When used as a decorator or with statement, the patch is undone on exit, even if an exception is raised.
- pytest: how to monkeypatch and mock modules and environments Checked 2026-10-11.
- Patch the name your own module uses rather than the original in the standard library.
- Patching standard-library functions or builtins can break pytest itself, so wrap an unavoidable one in monkeypatch.context().
- For code you control, passing dependencies in explicitly is suggested over patching them globally.