Why does pytest_runtest_call() cause my test to run twice when using item.runtest() inside it?

I’m trying to perform a global check in conftest.py using the pytest_runtest_call() hook.

Inside this hook, I’m calling item.runtest() to execute the test and then verify some captured output.

However, I’ve noticed that this causes each test to run twi,ce once by pytest itself, and again from my manual item.runtest() call.

This leads to issues such as:

  • Database state not being reset between runs, causing false test failures
  • Doubling of total test execution time

Here’s a simplified example:

def pytest_runtest_call(item):
    item.runtest()  # Causes second execution
    cap = item.funcargs["global_print_check"]
    out, err = cap.readouterr()
    assert out

What’s the correct way to implement pytest_runtest_call() so that the test only runs once? Should I avoid calling item.runtest() manually?

How can I perform additional assertions after the test without triggering a second run?

pytest_runtest_call(item) is the hook pytest itself uses to call your test.

When you call item.runtest() inside it, you’re effectively running the test a second time.

To fix this, simply remove that line, pytest already executes the test before or within this hook.

If you want to inspect or validate something after the test, use another hook like pytest_runtest_teardown() or store results in item’s attributes.

Instead of trying to assert inside pytest_runtest_call, use pytest_runtest_makereport() to analyze the result after each test run:

def pytest_runtest_makereport(item, call):
    if call.when == "call" and call.excinfo is None:
        cap = item.funcargs["global_print_check"]
        out, err = cap.readouterr()
        assert out

This ensures your check happens after the test runs once, without triggering it again.

A cleaner solution is to move your output validation into a fixture that runs automatically after each test:

@pytest.fixture(autouse=True)
def check_global_print(global_print_check):
    yield
    out, err = global_print_check.readouterr()
    assert out

This fixture executes once per test and avoids the double-execution problem caused by item.runtest().