Markers Reference¶
This page documents all pytest markers provided by pytest-test-categories.
Size Markers¶
pytest-test-categories provides four size markers to categorize tests based on Google’s “Software Engineering at Google” best practices.
@pytest.mark.small¶
@pytest.mark.small
def test_example():
"""Fast, hermetic unit test."""
pass
Signature: @pytest.mark.small
Description: Marks a test as a small (unit) test. Small tests are fast, hermetic, and test individual behaviors in isolation.
Characteristics:
Property |
Value |
|---|---|
Time Limit |
1 second |
Network Access |
Blocked |
Filesystem Access |
Blocked |
Subprocess Spawning |
Blocked |
Database Access |
Blocked |
Sleep Calls |
Blocked |
Target Distribution |
~80% of test suite |
Best Practices:
Test a single, specific behavior
Use test doubles (mocks, stubs, fakes) for external dependencies
No file I/O, network, or external dependencies
Run quickly and deterministically
Prefer state testing over interaction testing
Example:
import pytest
@pytest.mark.small
def test_add_numbers():
"""Unit test for addition function."""
assert add(2, 3) == 5
@pytest.mark.small
def test_validate_email_format():
"""Unit test for email validation."""
assert is_valid_email("user@example.com")
assert not is_valid_email("invalid-email")
@pytest.mark.medium¶
@pytest.mark.medium
def test_example():
"""Integration test with local services."""
pass
Signature: @pytest.mark.medium or @pytest.mark.medium(allow_external_systems=True)
Description: Marks a test as a medium (integration) test. Medium tests may use multiple threads, file I/O, and localhost network access.
Characteristics:
Property |
Value |
|---|---|
Time Limit |
300 seconds / 5 minutes |
Network Access |
Localhost only |
Filesystem Access |
Allowed |
Subprocess Spawning |
Allowed |
Database Access |
Allowed |
Sleep Calls |
Allowed |
Target Distribution |
~15% of test suite |
Parameters:
Parameter |
Type |
Default |
Description |
|---|---|---|---|
|
|
|
Suppress warnings when using testcontainers or Docker |
Best Practices:
Test integration between components
Use real databases (local or in-memory)
Allow multiple threads
Disallow external network access
Still test a single, specific behavior
Example:
import pytest
@pytest.mark.medium
def test_database_integration(tmp_path):
"""Test database operations with file storage."""
db_path = tmp_path / "test.db"
db = Database(db_path)
db.insert({"key": "value"})
assert db.get("key") == "value"
@pytest.mark.medium(allow_external_systems=True)
def test_with_testcontainers():
"""Test with Docker containers (suppresses external systems warning)."""
with PostgresContainer() as postgres:
# Test with real PostgreSQL
pass
@pytest.mark.large¶
@pytest.mark.large
def test_example():
"""End-to-end test with external services."""
pass
Signature: @pytest.mark.large
Description: Marks a test as a large (end-to-end) test. Large tests may access external networks and multiple machines.
Characteristics:
Property |
Value |
|---|---|
Time Limit |
900 seconds / 15 minutes |
Network Access |
Allowed |
Filesystem Access |
Allowed |
Subprocess Spawning |
Allowed |
Database Access |
Allowed |
Sleep Calls |
Allowed |
Target Distribution |
~2.5-5% of test suite |
Best Practices:
Reserve for system-level or end-to-end tests
Test complete user workflows
May access external services and APIs
May span multiple machines
Example:
import pytest
import requests
@pytest.mark.large
def test_full_api_workflow():
"""End-to-end test of API workflow."""
response = requests.post("https://api.example.com/users", json={"name": "Test"})
assert response.status_code == 201
user_id = response.json()["id"]
response = requests.get(f"https://api.example.com/users/{user_id}")
assert response.json()["name"] == "Test"
@pytest.mark.xlarge¶
@pytest.mark.xlarge
def test_example():
"""Extended test for massive integration."""
pass
Signature: @pytest.mark.xlarge
Description: Marks a test as an extra-large test. XLarge tests are for truly enormous features requiring extended execution time.
Characteristics:
Property |
Value |
|---|---|
Time Limit |
900 seconds / 15 minutes |
Network Access |
Allowed |
Filesystem Access |
Allowed |
Subprocess Spawning |
Allowed |
Database Access |
Allowed |
Sleep Calls |
Allowed |
Target Distribution |
0-5% of test suite |
Best Practices:
Use sparingly
Reserve for massive integration or performance tests
Consider if the test can be split into smaller tests
Example:
import pytest
@pytest.mark.xlarge
def test_performance_benchmark():
"""Performance test processing large dataset."""
dataset = generate_large_dataset(size=1_000_000)
result = process_dataset(dataset)
assert result.performance_score > 0.95
Base Test Classes¶
pytest-test-categories provides base test classes that automatically apply size markers through inheritance.
SmallTest¶
from pytest_test_categories import SmallTest
class TestMyFeature(SmallTest):
def test_example(self):
pass
Module: pytest_test_categories.test_bases
Description: Base class for small tests. Automatically applies @pytest.mark.small to all test methods in the class.
Class Attribute:
pytestmark = pytest.mark.small
Example:
from pytest_test_categories import SmallTest
class DescribeCalculator(SmallTest):
"""Small tests for Calculator class."""
def test_add_positive_numbers(self):
calc = Calculator()
assert calc.add(2, 3) == 5
def test_add_negative_numbers(self):
calc = Calculator()
assert calc.add(-2, -3) == -5
MediumTest¶
from pytest_test_categories import MediumTest
class TestIntegration(MediumTest):
def test_example(self):
pass
Module: pytest_test_categories.test_bases
Description: Base class for medium tests. Automatically applies @pytest.mark.medium to all test methods in the class.
Class Attribute:
pytestmark = pytest.mark.medium
Example:
from pytest_test_categories import MediumTest
class DescribeDatabaseIntegration(MediumTest):
"""Medium tests for database operations."""
def test_create_and_retrieve(self, tmp_path):
db = Database(tmp_path / "test.db")
db.create("user", {"name": "Alice"})
assert db.get("user")["name"] == "Alice"
LargeTest¶
from pytest_test_categories import LargeTest
class TestE2E(LargeTest):
def test_example(self):
pass
Module: pytest_test_categories.test_bases
Description: Base class for large tests. Automatically applies @pytest.mark.large to all test methods in the class.
Class Attribute:
pytestmark = pytest.mark.large
Example:
from pytest_test_categories import LargeTest
class DescribeAPIWorkflow(LargeTest):
"""Large tests for complete API workflows."""
def test_user_registration_flow(self, api_client):
# Complete end-to-end workflow
user = api_client.register(email="test@example.com")
api_client.verify_email(user.id)
session = api_client.login(email="test@example.com")
assert session.is_authenticated
XLargeTest¶
from pytest_test_categories import XLargeTest
class TestPerformance(XLargeTest):
def test_example(self):
pass
Module: pytest_test_categories.test_bases
Description: Base class for extra-large tests. Automatically applies @pytest.mark.xlarge to all test methods in the class.
Class Attribute:
pytestmark = pytest.mark.xlarge
Example:
from pytest_test_categories import XLargeTest
class DescribeLoadTesting(XLargeTest):
"""XLarge tests for load and performance."""
def test_concurrent_users(self, load_test_framework):
results = load_test_framework.run(
users=1000,
duration_seconds=300
)
assert results.error_rate < 0.01
Marker Inheritance and Composition¶
Class-Level Markers¶
Apply a marker to all tests in a class:
import pytest
@pytest.mark.medium
class TestDatabaseOperations:
def test_insert(self):
pass # Inherits @pytest.mark.medium
def test_query(self):
pass # Inherits @pytest.mark.medium
Module-Level Markers¶
Apply a marker to all tests in a module using pytestmark:
# test_integration.py
import pytest
pytestmark = pytest.mark.medium
def test_one():
pass # Has @pytest.mark.medium
def test_two():
pass # Has @pytest.mark.medium
Multiple Markers¶
Tests can have multiple markers, but only ONE size marker:
import pytest
@pytest.mark.small
@pytest.mark.slow # Custom marker, not a size marker
def test_example():
pass
Error - Multiple Size Markers:
# THIS WILL RAISE pytest.UsageError
@pytest.mark.small
@pytest.mark.medium
def test_invalid():
pass
pytest.UsageError: Test cannot have multiple size markers: ['small', 'medium']
Marker Precedence¶
When markers are applied at multiple levels, pytest’s standard precedence applies:
Function-level markers (highest priority)
Class-level markers
Module-level markers (lowest priority)
import pytest
pytestmark = pytest.mark.small # Module level
class TestExample:
pytestmark = pytest.mark.medium # Class level - overrides module
@pytest.mark.large # Function level - overrides class
def test_one(self):
pass # Uses @pytest.mark.large
def test_two(self):
pass # Uses @pytest.mark.medium
Unmarked Tests¶
Tests without a size marker will trigger a warning:
PytestWarning: Test has no size marker: tests/test_example.py::test_unmarked
Unmarked tests are allowed to run but are tracked separately in distribution statistics.
Best Practice: Always mark your tests with an appropriate size marker.
Source Code References¶
Component |
Location |
|---|---|
Size markers registration |
|
TestSize enum |
|
Base test classes |