Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Replaying failures

When a test fails, you want the failure to keep happening until you’ve fixed it, and you want to be able to rerun exactly the failing input. proptest has three tools for this: the example database, explicit examples, and reproduce blobs.

The example database

When a test fails, proptest saves the shrunk failing example in the example database: a directory, by default .proptest/examples under the working directory. The next time the test runs, proptest tries the saved examples first (the reuse phase). So a failure doesn’t disappear on the next run just because the random search went somewhere else. Once a saved example passes, it’s deleted.

Each test’s examples are kept under a key, which given and check take from the test’s source location (file:line:column). Moving a test to another line changes its key, and its saved examples are no longer found. To keep them, give the test a fixed key with Settings(database_key="parser round trip").

What’s saved is the example’s choice sequence: the choices the strategies made, one per line, not the value itself. It replays through whatever strategies the test uses now. So if you change the test’s strategies, a saved example may turn into a different value, or become invalid and be dropped.

Disabling the database

Pass Settings(database=None), or set PROPTEST_DATABASE=none in the environment to disable it for every test that uses the default. A test suite’s runner might do this in CI, where nothing is kept between runs anyway. You can also add .proptest/ to .gitignore.

Explicit examples

check_examples(examples, test) runs a test on specific inputs (Hypothesis’s @example). Use it for inputs that must always be tested, such as a regression from a bug report or a known edge case:

from proptest import check_examples, given, integers

def main():
    def small(x: Int) raises:
        if x > 100:
            raise Error("too big")

    try:
        check_examples([1, 5, 500], small)
    except e:
        print(e)
Falsifying explicit example (example.mojo:9:23):
    example(500)
too big

Explicit examples are not shrunk: the input is the one you gave.

Prefer explicit examples to the database for important cases

The database is a cache, not a test suite: a saved example is lost when the test’s strategies or location change, and it isn’t shared through version control. When a failure matters enough that it must never come back, add it as an explicit example.

Replaying a test case with reproduce

With Settings(print_blob=True), or verbose output, a failure report ends with a line like this:

Falsifying example (test.mojo:20:14):
    given(101)
    To reproduce this example, run the test with Settings(reproduce="proptest1:aToxMDEK")
too big

Running the test with Settings(reproduce="proptest1:aToxMDEK") replays exactly that example, without any search or shrinking (Hypothesis’s @reproduce_failure). The blob is the encoded choice sequence, so it’s small enough to paste into a bug report. Like a database entry, it only reproduces the failure while the test’s strategies stay the same.

Repeating a run with a seed

Verbose output also prints the run’s random seed. Settings(seed=...) or PROPTEST_SEED=... repeats the whole run, including the examples that passed. Settings(derandomize=True) makes every run of a test use the same seed, derived from its key.

Sharing failures

The database is a directory of small text files, so you can copy it between machines, or point several checkouts at one directory with PROPTEST_DATABASE=/shared/path. Keys are source locations, so the checkouts should be at the same version of the tests.

Stopping on the minimal example in a debugger

proptest calls before_minimal_test_case(name) just before it replays a shrunk failure for the last time. Set a breakpoint on it to stop only on the minimal failing example; see Debug a failing test.