Skip to main content

assert-pytest-test-can-fail

Assert that every pytest test is able to fail.

Why Must a Test Be Able to Fail?

A test earns its place by being able to fail. A test whose assertion is satisfied on every path it can reach runs, costs what it costs, and reports green for the whole life of the thing it claims to watch. Coverage counts it. The suite counts it. The only way anybody finds out is by reading it.

One shape produces this reliably. A test sets a flag to False, asks a remote service about a resource, sets the flag to True when the call succeeds, and sets the same flag to True again in the handler for the error that means the resource is absent. Every other error is re-raised. Then it asserts the flag. Every path that reaches the assertion has set it to True, so the assertion is decoration, and a genuinely missing resource is reported as present.

Mutation testing answers this question in general and answers it better, by mutating the code under test and seeing whether a test dies. It cannot answer it for a test with no code under test — one that asserts about a deployed resource through a client library — and it costs a suite run per mutant. This tool asks a narrower question that a parse can settle.

Installation

pip install assert-pytest-test-can-fail

Usage

Command Line

# Scan specific files
assert-pytest-test-can-fail test_example.py

# Scan directories recursively
assert-pytest-test-can-fail test/

# Use glob patterns
assert-pytest-test-can-fail "test/**/test_*.py"

# Exclude patterns
assert-pytest-test-can-fail test/ --exclude "**/conftest.py"

# Verbose output
assert-pytest-test-can-fail test/ --verbose

# Fail fast (exit on first finding)
assert-pytest-test-can-fail test/ --fail-fast

# Warn only (always exit 0)
assert-pytest-test-can-fail test/ --warn-only

GitHub Actions

- uses: 10U-Labs/assert-pytest-test-can-fail@latest
  with:
    files: "test/"
    verbose: "true"

As a Python Module

python -m assert_pytest_test_can_fail test/

Output Format

Default output shows one finding per line:

path/to/test_file.py:23:test_bucket_exists: every path to the assert sets the flag, so the test cannot fail

Format: file_path:line_number:function_name, followed by the reason. The line number is the assert statement.

Exit Codes

  • 0: No findings (or --warn-only specified)
  • 1: Findings detected
  • 2: Error (missing files, syntax errors, etc.)

What Is Reported

A function whose name begins with test is reported when all of these are true:

  • it contains a try statement with at least one except handler
  • the statement that ends the try body — or the else clause, when there is one — assigns True to a bare name
  • every handler on that try is closed: it ends in raise, or it ends assigning True to that same name, or it ends in an if/else whose two branches each do one of those
  • an assert on that bare name follows the whole try statement

The last one matters. An assertion sitting inside the try, or above it, is asking a different question, and is left alone.

Each reported function is reported once, at the first assertion that qualifies.

What Is Never Reported

The shape alone is not the defect, and a rule that says otherwise is a rule nobody keeps. A permission check written this way is correct: it asks whether the credentials may inspect a resource, a 404 answers yes, and any error code the handler does not name leaves the flag False, so the assertion fails.

def test_can_describe_bucket():
    has_permission = False
    try:
        client.head_bucket(Bucket=name)
        has_permission = True
    except ClientError as error:
        code = error.response["Error"]["Code"]
        if code == "403":
            pytest.fail("No permission to inspect the bucket")
        if code == "404":
            has_permission = True
    assert has_permission

That handler ends in an if with no else, so it can complete with the flag still False. It is not reported. Neither is a handler that ends in pass, nor a pytest.fail that leaves other codes falling through — only raise and an assignment on every branch close a handler.

Also never reported:

  • a try with no except handler at all, such as try/finally
  • a success path that computes the flag rather than asserting it, such as exists = client.bucket_exists(name)
  • a chained assignment, exists = asked = True, or an attribute target, record.exists = True
  • an assertion on anything but a bare name, such as assert count == 1
  • a try inside a nested function, a lambda or a class the test defines: the tool does not descend into a scope the test opens
  • a function whose name does not begin with test

Only files pytest would collect are scanned: test_*.py and *_test.py.

Options

Option Description
--exclude PATTERNS Glob patterns to exclude (comma-separated)
--quiet Suppress all output (exit code only)
--count Output only the number of findings
--verbose Show detailed processing information
--fail-fast Exit after first finding
--warn-only Always exit 0, even with findings

License

Apache 2.0 - See LICENSE.txt

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

assert_pytest_test_can_fail-20260908004717.tar.gz (27.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

File details

Details for the file assert_pytest_test_can_fail-20260908004717.tar.gz.

File metadata

File hashes

Hashes for assert_pytest_test_can_fail-20260908004717.tar.gz
Algorithm Hash digest
SHA256 650f3b7c6c8601354a48b722632787c830eea380b4fbde83ee4f690a511b9969
MD5 cfc78092996fd4c7b6535aff5862cade
BLAKE2b-256 2bc73ac26b9c367ae2e05c653c7ebf51982103c05f0e1fd661ee56dce14f7e77

See more details on using hashes here.

Provenance

The following attestation bundles were made for assert_pytest_test_can_fail-20260908004717.tar.gz:

Publisher: release.yml on 10U-Labs/assert-pytest-test-can-fail

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file assert_pytest_test_can_fail-20260908004717-py3-none-any.whl.

File metadata

File hashes

Hashes for assert_pytest_test_can_fail-20260908004717-py3-none-any.whl
Algorithm Hash digest
SHA256 bf6e398819373dcefa4cecd864347c10d126809dc371182de21d29ffdda93715
MD5 7655534de67067082c36687cc67c4ee8
BLAKE2b-256 2cfc3c7676f1e2fabfebec03c622933f30a8dea35b83053d3c6f8c3dfb79a869

See more details on using hashes here.

Provenance

The following attestation bundles were made for assert_pytest_test_can_fail-20260908004717-py3-none-any.whl:

Publisher: release.yml on 10U-Labs/assert-pytest-test-can-fail

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

20260908004717 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page