TS008: TestTimeout

Overview

Property Value
ID TS008
Name TestTimeout
Group tests
Severity NOTE

Description

Checks if test timeout configuration is present to prevent hanging tests from blocking CI/CD pipelines.

Tests without timeouts can:

  • Block CI indefinitely: A single hanging test can prevent deployments
  • Waste resources: Hung tests consume CI runner time
  • Hide issues: Deadlocks and infinite loops go undetected
  • Frustrate developers: Waiting for tests that never complete

What it checks

The check looks for timeout configuration in several places:

  1. pytest-timeout in dev dependencies: pyproject.toml optional-dependencies
  2. Timeout decorators in test files: @pytest.mark.timeout(...) decorators
  3. Timeout configuration in conftest.py: Global timeout settings

Result states

  • PASSED: Timeout configuration found
  • FAILED: No timeout configuration detected
  • NOT_APPLICABLE: No tests directory found
# Detected patterns
@pytest.mark.timeout(60)  # Per-test timeout
def test_slow_operation():
    pass

# In conftest.py
def pytest_configure(config):
    config.addinivalue_line("timeout", "300")

How to fix

Install pytest-timeout

The simplest approach is to add pytest-timeout to your dev dependencies:

# pyproject.toml
[project.optional-dependencies]
dev = [
    "pytest>=7.0",
    "pytest-timeout>=2.0",  # Add this
]

Then configure a default timeout:

# pyproject.toml
[tool.pytest.ini_options]
timeout = 300  # 5 minute default timeout

Use timeout decorators for specific tests

import pytest

@pytest.mark.timeout(10)  # 10 second timeout
def test_quick_operation():
    result = quick_function()
    assert result is not None

@pytest.mark.timeout(60)  # 1 minute for slower tests
def test_slow_operation():
    result = slow_function()
    assert result is not None

@pytest.mark.timeout(0)  # Disable timeout for specific test
def test_known_slow():
    result = very_slow_but_necessary()
    assert result is not None

Configure timeout in conftest.py

# conftest.py
import pytest

def pytest_configure(config):
    """Configure default timeout for all tests."""
    config.addinivalue_line(
        "markers",
        "timeout(seconds): Set test timeout"
    )

@pytest.fixture(autouse=True)
def default_timeout(request):
    """Apply default timeout if not specified."""
    if not request.node.get_closest_marker("timeout"):
        request.node.add_marker(pytest.mark.timeout(60))

Use pytest.ini or pyproject.toml

# pytest.ini
[pytest]
timeout = 300
timeout_method = thread

Or in pyproject.toml:

[tool.pytest.ini_options]
timeout = 300
timeout_method = "thread"

Set timeout via CLI

# Run with 60 second timeout
pytest --timeout=60

# Run with timeout and method
pytest --timeout=60 --timeout-method=thread

Timeout methods

pytest-timeout supports different timeout methods:

  • thread (default): Uses a separate thread to monitor timeout
  • signal: Uses SIGALRM (Unix only, more reliable)
[tool.pytest.ini_options]
timeout = 300
timeout_method = "signal"  # More reliable on Unix

Handling legitimate slow tests

Some tests genuinely need more time:

import pytest

# Mark with longer timeout
@pytest.mark.timeout(600)  # 10 minutes
def test_integration_suite():
    pass

# Or disable timeout for specific test
@pytest.mark.timeout(0)
def test_known_slow():
    pass

# Group slow tests for separate runs
@pytest.mark.slow
@pytest.mark.timeout(300)
def test_performance():
    pass

Run slow tests separately:

# Skip slow tests in regular runs
pytest -m "not slow" --timeout=60

# Run slow tests with longer timeout
pytest -m slow --timeout=600

Configuration

Skip this check

[tool.pycmdcheck]
skip = ["TS008"]

CLI

pycmdcheck --skip TS008

Why NOTE severity?

This check is a NOTE because:

  • Small projects may not need timeouts
  • Local development often doesn’t require timeout enforcement
  • Some test suites have custom timeout handling

However, timeouts are strongly recommended for:

  • CI/CD pipelines
  • Large test suites
  • Integration tests
  • Any tests involving I/O or network

References