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
# 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 |