Saltar a contenido

Hoja de Referencia de pytest

Instalación

Platform Comando
pip (All platforms) pip install pytest
Ubuntu/Debian sudo apt install python3-pytest
Fedora/RHEL sudo dnf install python3-pytest
Arch Linux sudo pacman -S python-pytest
macOS (Homebrew) brew install pytest
Windows (Chocolatey) choco install pytest
Virtual Environment python -m venv venv && source venv/bin/activate && pip install pytest
With Common Plugins pip install pytest pytest-cov pytest-xdist pytest-mock pytest-html
Verify Installation pytest --version
## Comandos Básicos
Comando Descripción
pytest Ejecutar todas las pruebas en el directorio actual y subdirectorios
pytest test_file.py Ejecutar pruebas en un archivo específico
pytest tests/ Ejecutar todas las pruebas en un directorio específico
pytest test_file.py::test_function Ejecutar una función de prueba específica
pytest test_file.py::TestClass Ejecutar todas las pruebas en una clase específica
pytest test_file.py::TestClass::test_method Ejecutar un método de prueba específico en una clase
pytest -v Ejecutar pruebas con salida detallada (mostrar nombres de pruebas)
pytest -vv Ejecutar pruebas con salida muy detallada (mostrar detalles completos)
pytest -q Ejecutar pruebas en modo silencioso (salida mínima)
pytest -s Mostrar declaraciones de impresión y stdout durante la ejecución de pruebas
pytest -x Detener después del primer fallo de prueba
pytest --maxfail=3 Detener después de N fallos de prueba
pytest -k "test_user" Ejecutar pruebas que coincidan con la expresión de palabra clave
pytest -k "user and not admin" Ejecutar pruebas que coincidan con múltiples palabras clave (lógica AND/NOT)
pytest -m "slow" Ejecutar pruebas marcadas con un marcador específico
pytest -m "not slow" Ejecutar pruebas excluyendo marcador específico
pytest --collect-only Mostrar qué pruebas se ejecutarían sin ejecutarlas
pytest --lf Ejecutar solo pruebas que fallaron en la última ejecución (últimos fallidos)
pytest --ff Ejecutar primero las pruebas fallidas, luego las otras (fallidas primero)
pytest -l Mostrar variables locales en trazas de pila en caso de fallo
## Uso Avanzado
Comando Descripción
pytest -n auto Ejecutar pruebas en paralelo utilizando todos los núcleos de CPU disponibles (requiere pytest-xdist)
pytest -n 4 Ejecutar pruebas en paralelo con 4 procesos worker
pytest --durations=10 Mostrar las 10 duraciones de prueba más lentas
pytest --durations=10 --durations-min=1.0 Mostrar pruebas más lentas que toman al menos 1 segundo
pytest --cov=myproject Ejecutar pruebas con informe de cobertura de código (requiere pytest-cov)
pytest --cov=myproject --cov-report=html Generar informe de cobertura HTML en el directorio htmlcov/
pytest --cov=myproject --cov-report=term-missing Mostrar cobertura con números de línea faltantes en terminal
pytest --cov=myproject --cov-fail-under=80 Falla si la cobertura está por debajo del 80%
pytest --cov-branch Incluir análisis de cobertura de ramas
pytest --junitxml=report.xml Generar informe XML de JUnit para integración de CI/CD
pytest --html=report.html Generar informe de pruebas HTML (requiere pytest-html)
pytest --pdb Entrar en el depurador de Python (PDB) en caso de fallos de pruebas
pytest --trace Caer en PDB al inicio de cada prueba
pytest --setup-show Mostrar configuración y desmontaje de fixtures durante la ejecución
pytest --fixtures Enumerar todos los fixtures disponibles y sus docstrings
pytest --markers Listar todos los marcadores registrados
pytest --tb=short Usar formato de traceback más corto
pytest --tb=line Mostrar una línea por cada fallo en el traceback
pytest --tb=no Deshabilitar la salida de traceback
pytest -W error::DeprecationWarning Tratar las advertencias de deprecación como errores
pytest --capture=no Deshabilitar la captura de salida (igual que -s)
pytest --timeout=300 Establecer un tiempo de espera de 300 segundos por prueba (requiere pytest-timeout)
pytest --count=3 Repite cada prueba 3 veces (requiere pytest-repeat)
pytest --random-order Ejecutar pruebas en orden aleatorio (requiere pytest-random-order)
pytest -ra Mostrar resumen breve de pruebas para todas las pruebas (pasadas, fallidas, omitidas, etc.)
## Configuración

Archivo de Configuración pytest.ini

Colocar en el directorio raíz del proyecto:

[pytest]
# Minimum pytest version required
minversion = 7.0

# Directories to search for tests
testpaths = tests

# Test file patterns
python_files = test_*.py *_test.py

# Test class patterns
python_classes = Test* *Tests

# Test function patterns
python_functions = test_*

# Default command line options
addopts = 
    -ra
    --strict-markers
    --strict-config
    --verbose
    --cov=myproject
    --cov-report=html
    --cov-report=term-missing

# Custom markers
markers =
    slow: marks tests as slow (deselect with '-m "not slow"')
    integration: marks tests as integration tests
    unit: marks tests as unit tests
    smoke: marks tests as smoke tests
    database: marks tests requiring database connection
    api: marks tests for API testing

# Directories to ignore
norecursedirs = .git .tox dist build *.egg venv node_modules

# Warning filters
filterwarnings =
    error
    ignore::UserWarning
    ignore::DeprecationWarning

# Logging configuration
log_cli = true
log_cli_level = INFO
log_cli_format = %(asctime)s [%(levelname)s] %(message)s
log_cli_date_format = %Y-%m-%d %H:%M:%S

Configuración pyproject.toml

Proyectos de Python modernos usando pyproject.toml:

[tool.pytest.ini_options]
minversion = "7.0"
testpaths = ["tests"]
python_files = ["test_*.py", "*_test.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]

addopts = [
    "-ra",
    "--strict-markers",
    "--cov=myproject",
    "--cov-branch",
    "--cov-report=html",
    "--cov-report=term-missing:skip-covered",
    "--cov-fail-under=80",
]

markers = [
    "slow: marks tests as slow",
    "integration: integration tests",
    "unit: unit tests",
    "smoke: smoke tests",
]

filterwarnings = [
    "error",
    "ignore::UserWarning",
]

conftest.py - Fixtures Compartidos

Colocar en el directorio raíz de pruebas para fixtures compartidos:

import pytest

# Session-scoped fixture (runs once per test session)
@pytest.fixture(scope="session")
def database():
    """Provide database connection for entire test session"""
    db = create_database_connection()
    yield db
    db.close()

# Module-scoped fixture (runs once per test module)
@pytest.fixture(scope="module")
def api_client():
    """Provide API client for test module"""
    client = APIClient()
    yield client
    client.cleanup()

# Function-scoped fixture (default, runs for each test)
@pytest.fixture
def sample_data():
    """Provide sample data for testing"""
    return {"id": 1, "name": "Test User"}

# Autouse fixture (automatically used by all tests)
@pytest.fixture(autouse=True)
def reset_state():
    """Reset application state before each test"""
    clear_cache()
    yield
    cleanup_resources()

# Parametrized fixture
@pytest.fixture(params=["sqlite", "postgres", "mysql"])
def db_type(request):
    """Test with multiple database types"""
    return request.param

# Configure pytest hooks
def pytest_configure(config):
    """Add custom configuration"""
    config.addinivalue_line(
        "markers", "custom: custom marker description"
    )

Casos de Uso Comunes

Caso de Uso 1: Pruebas Unitarias Básicas

# Create test file
cat > test_calculator.py << 'EOF'
def add(a, b):
    return a + b

def test_add():
    assert add(2, 3) == 5
    assert add(-1, 1) == 0
    assert add(0, 0) == 0
EOF

# Run the tests
pytest test_calculator.py -v

Caso de Uso 2: Pruebas con Fixtures

# Create test with fixtures
cat > test_user.py << 'EOF'
import pytest

@pytest.fixture
def user_data():
    return {"username": "testuser", "email": "test@example.com"}

def test_user_creation(user_data):
    assert user_data["username"] == "testuser"
    assert "@" in user_data["email"]
EOF

# Run tests with fixture details
pytest test_user.py -v --setup-show

Caso de Uso 3: Pruebas Parametrizadas

# Create parametrized tests
cat > test_math.py << 'EOF'
import pytest

@pytest.mark.parametrize("input,expected", [
    (2, 4),
    (3, 9),
    (4, 16),
    (5, 25),
])
def test_square(input, expected):
    assert input ** 2 == expected
EOF

# Run parametrized tests
pytest test_math.py -v

Caso de Uso 4: Pruebas de Integración con Marcadores

# Create tests with markers
cat > test_api.py << 'EOF'
import pytest

@pytest.mark.unit
def test_data_validation():
    assert True

@pytest.mark.integration
def test_api_endpoint():
    # Simulated API test
    assert True

@pytest.mark.slow
@pytest.mark.integration
def test_full_workflow():
    # Long-running test
    assert True
EOF

# Run only unit tests
pytest test_api.py -m unit -v

# Run integration tests excluding slow ones
pytest test_api.py -m "integration and not slow" -v

Caso de Uso 5: Generación de Informe de Cobertura

# Run tests with coverage and generate reports
pytest --cov=myproject --cov-report=html --cov-report=term-missing

# View coverage report
# HTML report will be in htmlcov/index.html

# Run with coverage threshold
pytest --cov=myproject --cov-fail-under=80

# Generate coverage badge
pytest --cov=myproject --cov-report=term --cov-report=html

Mejores Prácticas

  • Usar nombres de pruebas descriptivos: Nombrar pruebas con test_prefijo y describir lo que prueban (por ejemplo, test_user_registration_with_valid_email)
  • Seguir el patrón AAA: Estructurar pruebas con secciones de Arrange (preparación), Act (ejecución), Assert (verificación) para mayor claridad
  • Usar fixtures para configuración/limpieza: Aprovechar fixtures de pytest en lugar de métodos de configuración/limpieza para mejor reutilización e inyección de dependencias
  • Marcar pruebas apropiadamente: Usar marcadores (@pytest.mark.slow, @pytest.mark.integration) para categorizar pruebas y permitir ejecución selectiva
  • Mantener pruebas aisladas: Cada prueba debe ser independiente y no depender del estado de otras pruebas; usar fixtures con ámbitos apropiados
  • Usar parametrize para pruebas similares: En lugar de escribir múltiples pruebas similares, usar @pytest.mark.parametrizepara probar múltiples entradas
  • Configurar pytest.ini o pyproject.toml: Establecer valores predeterminados de proyecto para descubrimiento de pruebas, marcadores y opciones de línea de comandos en archivos de configuración
  • Escribir aserciones enfocadas: Usar aserciones simples y claras; la introspección de pytest muestra automáticamente información detallada de fallos
  • Usar conftest.py para fixtures compartidos: Colocar fixtures reutilizables en archivos conftest.pyen los niveles de directorio apropiados
  • Ejecutar pruebas frecuentemente: Ejecutar pruebas durante el desarrollo con pytest -xpara detenerse en el primer fallo para retroalimentación más rápida
  • Monitorear cobertura de pruebas: Revisar regularmente informes de cobertura y apuntar a 80%+ de cobertura, pero enfocarse en pruebas significativas sobre el porcentaje

Resolución de Problemas

Problema Solución
Tests not discovered Ensure files match patterns: test_*.py or *_test.py, functions start with test_, classes start with Test
Import errors in tests Add empty __init__.py files in test directories, or install package in editable mode: pip install -e .
Fixture not found Check fixture is defined in same file or conftest.py, verify correct scope, ensure fixture name matches parameter
Tests pass locally but fail in CI Check for environment-specific dependencies, ensure consistent Python versions, verify all dependencies in requirements.txt
Slow test execution Use pytest --durations=10 to identify slow tests, consider parallel execution with pytest -n auto, mark slow tests with @pytest.mark.slow
Coverage not working Install pytest-cov: pip install pytest-cov, ensure source path is correct: --cov=myproject, check .coveragerc configuration
Markers not recognized Register markers in pytest.ini or pyproject.toml under [tool.pytest.ini_options], use --strict-markers to catch typos
PDB not working with capture Use pytest -s --pdb to disable output capturing, or use pytest.set_trace() instead of pdb.set_trace()
Fixtures running in wrong order Check fixture scope (session > module > class > function), use @pytest.fixture(autouse=True) carefully, review dependency chain
Parallel tests failing Asegúrate de que las pruebas estén aisladas y no compartan estado, verifica las condiciones de carrera, usa bloqueos adecuados para recursos compartidos
Memory leaks in tests Use @pytest.fixture(scope="function") for proper cleanup, ensure fixtures yield and cleanup properly, check for circular references