ScreenshotNeo

BlogHow-to

How to Set Timeouts in Pytest

Install pytest-timeout to limit individual tests or an entire suite. Configure seconds, fixture scope, timeout methods, and recoverable failure behavior.

By the ScreenshotNeo team4 October 20267 min read

Pytest does not include this timeout mechanism in its core. Install the pytest-timeout plugin, then set a default with --timeout=SECONDS or the timeout project setting. Use @pytest.mark.timeout(SECONDS) to set a limit on an individual test. Timeouts are intended to catch hangs and excessively long tests, not to measure performance precisely. The plugin documentation describes the supported options and their behavior.

Install and set a timeout

Install the plugin in the same Python environment that runs pytest. Pytest automatically discovers installed plugins.

python -m pip install pytest-timeout
python -m pytest --timeout=30

The number is seconds. This command gives tests a default 30-second limit. Choose a value based on the expected work and environment; 30 seconds is an example, not a universal recommendation.

Set a project-wide default

To apply a default each time the project runs, add timeout to the pytest configuration file. For pytest.ini:

[pytest]
timeout = 30

The same setting can be placed in the corresponding pytest section of other supported configuration formats, such as pyproject.toml or tox.ini. Keep the setting under pytest’s configuration section and use the syntax for that file format.

Limit one test or override the default

import pytest

@pytest.mark.timeout(5)
def test_may_hang():
    result = operation_that_might_stall()
    assert result is not None

A marker sets the timeout for that test. A zero timeout disables the timeout for the marked item. You can also set the timeout method or fixture scope on the marker; those options are covered below.

Choose how the timeout is supplied

The plugin supports a project configuration value, an environment variable, a command-line option, and a per-test marker. Its documented precedence is:

  1. Configuration file
  2. PYTEST_TIMEOUT environment variable
  3. --timeout command-line option
  4. @pytest.mark.timeout(...) on an individual test

For example, a project default can be overridden for a particular test with its marker. Because configuration and environment values may affect a developer’s local run differently from CI, check the effective settings when behavior differs across environments.

Environment variable example

PYTEST_TIMEOUT=20 python -m pytest

On Windows PowerShell, set the variable for the command this way:

$env:PYTEST_TIMEOUT = "20"
python -m pytest

Understand what the timer includes

By default, a test’s timeout covers more than the statements inside its test function. It normally includes setup, test execution, and relevant fixture finalizers. Shared fixtures can complicate which test’s time includes their setup or teardown, because fixture scope and reuse determine when those phases run.

Limit only the test function body

If fixture setup is intentionally outside the time budget, use timeout_func_only in the project configuration:

[pytest]
timeout = 30
timeout_func_only = true

Or set it for one marked test:

import pytest

@pytest.mark.timeout(5, func_only=True)
def test_function_body_only(database_fixture):
    assert database_fixture.is_ready()

The marker’s func_only value controls the behavior for that decorated test. If a project enables function-only timing globally and a particular marked test should use the default broader scope, specify func_only=False explicitly. Consult the plugin documentation for details about fixture lifetimes and the test phases covered by the installed release.

Select a timeout method

pytest-timeout provides signal and thread methods. The method affects platform support and what pytest can do after a timeout.

Method Behavior Trade-off
signal Uses SIGALRM where supported. It can interrupt the test and allow the pytest process to continue. Not available everywhere; can conflict with code that also uses SIGALRM.
thread Uses a watchdog thread and can terminate the process when a test exceeds its limit. More portable, but process termination can prevent normal fixture cleanup and JUnit XML output.

On POSIX systems where SIGALRM is available, signal is the default. The plugin uses thread where the signal method is unavailable, and documents it as the safer choice when it is not running in the main thread. Neither method guarantees graceful recovery in every situation.

Set the method for a run

python -m pytest --timeout=30 --timeout-method=thread

The older --timeout_method spelling is deprecated in favor of --timeout-method. You can set a project default with timeout_method:

[pytest]
timeout = 30
timeout_method = thread

Set the method on a test

import pytest

@pytest.mark.timeout(10, method="thread")
def test_network_call_has_a_limit():
    assert call_service()

Use the signal method when its platform and signal handling suit the test process and you want pytest to be able to continue after an interruption. Use the thread method when broader portability or running outside the main thread matters, while accounting for possible process termination and missing teardown or report output.

Use a session timeout for a suite-level budget

A session timeout is different from a per-test timeout. The plugin checks the session limit between tests. It stops further tests when the budget has expired, but does not interrupt a currently running test. Pair it with a per-test timeout if an individual test must not hang indefinitely.

python -m pytest --session-timeout=600 --timeout=30

To configure the session default in pytest.ini:

[pytest]
session_timeout = 600
timeout = 30

The session limit is useful for bounding how long a run continues across many tests; it does not provide a hard wall-clock limit for the entire process because a test already in progress must finish or hit its own per-test timeout.

Keep timeouts useful and predictable

  • Use them as hang protection. The plugin is designed for excessively long or deadlocked tests, not precise timings or performance regressions. For performance tracking, use a dedicated measurement approach that accounts for noise.
  • Allow for environment variation. CI machines, developer laptops, network services, and loaded shared runners have different timing. Set limits to catch abnormal stalls without turning ordinary variance into failures.
  • Investigate the underlying slow path. A timeout can reveal a deadlock, unbounded wait, unavailable dependency, or unexpectedly expensive setup. It does not explain which one occurred by itself.
  • Be deliberate with fixture scope. If fixture setup belongs in the test’s budget, retain the default. Use function-only mode only when excluding fixture time is intentional.
  • Expect different cleanup behavior by method. Signal interruption may allow pytest to proceed on supported systems, but signal conflicts are possible. Thread-triggered process termination may skip finalizers and report generation.
  • Check version behavior. Plugin releases and supported Python and pytest versions can change. Verify options against the installed version’s documentation and python -m pytest --help.

Troubleshooting

Symptom Likely cause What to do
pytest: error: unrecognized arguments: --timeout The plugin is not installed in the Python environment running pytest, or it did not load. Install with python -m pip install pytest-timeout using that same interpreter. Run pytest as python -m pytest and inspect python -m pytest --help.
A test times out while a fixture is starting or cleaning up The default timeout includes setup, execution, and relevant finalizers. Raise the budget if the work is expected, optimize the fixture, or intentionally use func_only=True to time only the test function.
The process exits and JUnit XML or teardown output is missing The thread method may terminate the process to stop a stuck test. Use signal mode where supported and safe for the code, or accept that hard termination may bypass normal cleanup and report generation. Preserve logs outside finalizers if they are needed for diagnosis.
Signal-related code behaves unexpectedly The signal method uses SIGALRM, which application code or another library may also manage. Choose --timeout-method=thread and account for its process-termination behavior.
A session timeout does not stop the current long test Session timeouts are checked between tests. Set a per-test --timeout or marker as well.
A test has no timeout despite a project default A higher-precedence source or a marker value of zero may change or disable the limit. Check the configuration file, PYTEST_TIMEOUT, command line, and marker in precedence order.
Debugging triggers an unexpected timeout Debugger detection may not recognize the active debugger. Review the plugin’s debugger detection options for the installed version; its documentation describes disabling detection when needed.

Or skip the browser setup

If the reason you are automating a browser is to capture a page for a test artifact or visual check, ScreenshotNeo offers a one-call screenshot API. This does not replace pytest timeouts; it removes the need to install and manage a browser just to capture a screenshot. See the ScreenshotNeo API documentation.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
  • Cookie banners, newsletter popups, and chat widgets are removed before the shot.
  • Bot checks, blank pages, and failed loads are never billed.
  • An MCP server lets AI agents use screenshot tools.
  • 1,000 screenshots a month are free with no card; paid plans start at $5 for 3,000.

Sign up for 1,000 free screenshots a month, with no card required.

FAQ

Does pytest itself have a built-in test timeout?

Pytest core does not provide the timeout behavior covered here. Install the third-party pytest-timeout plugin.

Does a timeout tell me whether my test is too slow?

It tells you that the configured limit was exceeded. It is a guard against hangs and excessive duration, not a precise performance measurement.

Can I use a suite timeout instead of a per-test timeout?

Use a session timeout to stop starting more tests after an overall budget expires. It will not interrupt a test that is already running, so it cannot replace a per-test limit for hang protection.

Why might cleanup not run after a timeout?

The thread method can terminate the process. When that happens, pytest cannot guarantee fixture finalizers or report output will complete.

References: pytest-timeout documentation, plugin options and implementation, and pytest plugin installation documentation.