pytest es un framework de pruebas maduro y completo para Python, reconocido por su flexibilidad y facilidad de uso. Entre sus características destacan:
- Documentación abundante y curva de arpendizaje suave.
- Soporte para parametrización que permite control detallado de casos de prueba.
- Idóneo para pruebas unitarias simples y funcionales complejas, así como para automatización con herramientas como Selenium o para APIs.
- Amplia gama de plugins de terceros, como pytest-selenium, pytest-html y otros.
- Gestión de pruebas omitidas o marcadas como xfail.
- Integración sencilla con sistemas de CI como Jenkins.
Convenciones de escritura:
- Archivos de prueba deben iniciar con test_ o terminar con _test.
- Clases de prueba empiezan con Test y no deben definir el método __init__.
- Funciones de prueba deben iniciar con test_.
- Las aserciones se realizan con la palabra clave assert.
Ejemplo Inicial
Archivo: ejemplo_pruebas.py
import pytest
class CasosDePrueba:
def test_cadena_contiene_letra(self):
texto = "ejemplo"
assert 'p' in texto
def test_atributo_disponible(self):
datos = "mundo"
assert hasattr(datos, 'split')
def test_subcadena_presente(self):
sub = "hola"
cadena = "hola python"
assert sub in cadena
Ejecución desde terminal:
cd al_directorio
pytest ejemplo_pruebas.py
Para visualizar una barra de progreso, instale el plugin pytest-sugar.
Configuración en PyCharm:
- Acceda a File -> Settings -> Tools -> Python Integrated Tools.
- Seleccione el nombre del proyecto y cambie Default test runner a pytest.
Ejecución programática:
if __name__ == "__main__":
pytest.main(['-q', 'ejemplo_pruebas.py'])
Parámetros de Consola
- -v: Muestra resultados detallados por función.
- -q: Presenta solo el resumen global.
- -s: Muestra salidas de print() en pruebas.
- -x: Detiene en el primer fallo.
- -m: Filtra pruebas por decoradores.
- -h: Muestra ayuda.
Ejemplos de comandos:
pytest # Ejecuta todas las pruebas en el directorio
pytest modulo.py # Ejecuta pruebas en el archivo
pytest -k "expresion" # Filtra por nombre
pytest modulo.py::funcion # Ejecuta prueba específica
Parametrización
Uso de @pytest.mark.parametrize():
Parámetro individual:
import pytest
import random
@pytest.mark.parametrize('entrada', [7, 14, 21])
def test_comparar_valor(entrada):
aleatorio = random.randint(1, 30)
assert entrada != aleatorio
Múltiples parámetros:
import pytest
@pytest.mark.parametrize('x, y, resultado', [
(2, 2, 4),
(5, 3, 8),
("a", "b", "ab"),
])
def test_operacion(x, y, resultado):
if isinstance(x, str):
res = x + y
else:
res = x + y
assert res == resultado
Orden de Ejecución
Instale pytest-ordering:
pip install pytest-ordering
Controle el orden con @pytest.mark.run(order=n):
import pytest
global_estado = []
@pytest.mark.run(order=2)
def test_segunda_etapa():
global_estado.append("segundo")
assert len(global_estado) == 2
@pytest.mark.run(order=1)
def test_primera_etapa():
global_estado.append("primero")
assert global_estado[0] == "primero"
Reportes HTML
Instale pytest-html:
pip install -U pytest-html
Generación de reporte:
pytest ejemplo_pruebas.py --html=informe.html
Cobertura de Código
Instale pytest-cov:
pip install pytest-cov
Uso:
pytest --cov-report=html --cov=./ src_pruebas
Parámetros relevantes:
- --cov=[ruta]: Define la ruta para medir cobertura.
- --cov-report=tipo: Formato del reporte (html, xml, etc.).
- --no-cov-on-fail: Omite reporte si hay fallos.
- --cov-fail-under=MIN: Exige un mínimo de cobertura.
Ejecución Paralela
Instale pytest-xdist:
pip install -U pytest-xdist
Ejecución con múltiples procesos:
pytest ejemplo_pruebas.py -n 4 # Usa 4 procesos
Reintentos de Pruebas Fallidas
Instale pytest-rerunfailures:
pip install pytest-rerunfailures
Ejemplo con posible fallo:
import random
def dividir(a, b):
return a / b
def test_division_segura():
divisor = random.choice([1, 2, 0])
if divisor == 0:
pytest.skip("Evitar división por cero")
assert dividir(10, divisor) > 0
Uso desde consola:
pytest --reruns 2 # Reintenta dos veces
pytest --reruns 2 --reruns-delay 3 # Con 3 segundos de pausa
Alternativamente, use el decorador @pytest.mark.flaky(reruns=2, reruns_delay=3).