Python
Tutorial Python: Testing con pytest
Aprende a instalar, configurar y escribir tests efectivos con pytest en Python. Domina aserciones, fixtures y buenas prácticas de testing.
Aprende Python y certifícateInstalación y configuración de pytest
Pytest es uno de los frameworks de testing más populares y potentes para Python. Su diseño minimalista y su capacidad para escribir tests de forma sencilla lo han convertido en la herramienta preferida por muchos desarrolladores para realizar pruebas unitarias y funcionales.
Para comenzar a utilizar pytest, primero necesitamos instalarlo en nuestro entorno de desarrollo. La forma más común es mediante el gestor de paquetes pip, que viene incluido con las instalaciones modernas de Python.
Instalación básica
La instalación de pytest es muy sencilla. Abre una terminal o línea de comandos y ejecuta:
pip install pytest
Para verificar que la instalación se ha completado correctamente, puedes comprobar la versión instalada:
pytest --version
Esto mostrará la versión de pytest que has instalado, confirmando que está listo para usarse.
Instalación en entornos virtuales
Es una buena práctica utilizar entornos virtuales para aislar las dependencias de cada proyecto. Para crear un entorno virtual e instalar pytest en él:
# Crear un entorno virtual
python -m venv venv
# Activar el entorno virtual
# En Windows:
venv\Scripts\activate
# En macOS/Linux:
source venv/bin/activate
# Instalar pytest en el entorno virtual
pip install pytest
Instalación de plugins
Una de las grandes ventajas de pytest es su ecosistema de plugins que extienden su funcionalidad. Algunos plugins populares incluyen:
- pytest-cov: Para medir la cobertura de código
- pytest-xdist: Para ejecutar tests en paralelo
- pytest-mock: Para facilitar el uso de mocks en los tests
Para instalar un plugin, simplemente usa pip:
pip install pytest-cov
Configuración básica
Pytest está diseñado para funcionar con una configuración mínima. Por defecto, buscará archivos con el patrón test_*.py
o *_test.py
en el directorio actual y sus subdirectorios.
Sin embargo, puedes personalizar su comportamiento mediante un archivo de configuración. Crea un archivo llamado pytest.ini
en la raíz de tu proyecto:
[pytest]
testpaths = tests
python_files = test_*.py
python_classes = Test*
python_functions = test_*
Este archivo configura:
- testpaths: Directorios donde buscar tests
- python_files: Patrón de nombres de archivos de test
- python_classes: Patrón de nombres de clases de test
- python_functions: Patrón de nombres de funciones de test
Configuración avanzada con pyproject.toml
En proyectos modernos de Python, puedes usar pyproject.toml
para configurar pytest, lo que sigue las recomendaciones de PEP 518 y PEP 621:
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = "test_*.py"
python_classes = "Test*"
python_functions = "test_*"
markers = [
"slow: marks tests as slow (deselect with '-m \"not slow\"')",
"integration: marks tests as integration tests",
]
Esta configuración incluye markers (etiquetas) que permiten categorizar tus tests y ejecutarlos selectivamente.
Configuración de entorno de desarrollo
Para una experiencia de desarrollo óptima, es recomendable configurar tu editor o IDE para trabajar con pytest:
VSCode
En Visual Studio Code, puedes instalar la extensión "Python" de Microsoft y configurar pytest como tu framework de testing predeterminado:
- Abre la configuración (Ctrl+,)
- Busca "python.testing.pytestEnabled"
- Establece su valor a
true
PyCharm
PyCharm tiene soporte integrado para pytest:
- Ve a File > Settings > Tools > Python Integrated Tools
- En la sección "Testing", selecciona "pytest" como Default test runner
Opciones de línea de comandos
Pytest ofrece numerosas opciones de línea de comandos para personalizar la ejecución de tests:
- Mostrar información detallada:
pytest -v
- Detener la ejecución después del primer fallo:
pytest -x
- Ejecutar solo tests que coincidan con un patrón:
pytest -k "nombre_test"
- Ejecutar tests con una etiqueta específica:
pytest -m "integration"
- Generar un informe de cobertura (requiere pytest-cov):
pytest --cov=mi_paquete
Integración con herramientas de CI/CD
Pytest se integra fácilmente con sistemas de integración continua como GitHub Actions, Travis CI o Jenkins. Por ejemplo, para GitHub Actions, puedes crear un archivo .github/workflows/tests.yml
:
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pytest pytest-cov
pip install -e .
- name: Test with pytest
run: |
pytest --cov=./ --cov-report=xml
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
Este workflow ejecutará tus tests automáticamente cada vez que hagas push o crees un pull request, y subirá los resultados de cobertura a Codecov.
Configuración para proyectos grandes
En proyectos de mayor escala, es útil organizar los tests en una estructura clara y configurar pytest para manejar dependencias complejas:
mi_proyecto/
├── src/
│ └── mi_paquete/
│ ├── __init__.py
│ └── modulo.py
├── tests/
│ ├── conftest.py
│ ├── unit/
│ │ └── test_modulo.py
│ └── integration/
│ └── test_integracion.py
└── pyproject.toml
El archivo conftest.py
es especialmente útil para definir fixtures compartidas entre múltiples tests:
import pytest
from pathlib import Path
@pytest.fixture
def data_dir():
"""Fixture que proporciona la ruta al directorio de datos de prueba."""
return Path(__file__).parent / "data"
@pytest.fixture(scope="session")
def db_connection():
"""Fixture que proporciona una conexión a la base de datos de prueba."""
# Configuración
connection = create_test_db_connection()
yield connection
# Limpieza
connection.close()
Creación de tests
Una vez configurado pytest en nuestro entorno, el siguiente paso es aprender a escribir tests efectivos. La creación de tests en pytest sigue una filosofía simple pero potente que facilita tanto la escritura como el mantenimiento del código de prueba.
Estructura básica de un test
En pytest, un test es simplemente una función cuyo nombre comienza con test_
. Esta convención permite a pytest identificar automáticamente qué funciones debe ejecutar como tests:
# archivo: test_ejemplo.py
def test_suma():
resultado = 2 + 2
assert resultado == 4
La palabra clave assert
es fundamental en pytest. A diferencia de otros frameworks que requieren métodos especiales de aserción, pytest aprovecha la instrucción assert
nativa de Python y proporciona mensajes de error detallados cuando fallan las aserciones.
Organización de tests
Existen dos enfoques principales para organizar tus tests:
- Funciones de test: Ideal para tests simples y directos
- Clases de test: Útil para agrupar tests relacionados y compartir configuración
Enfoque funcional
# test_calculadora.py
def test_suma():
assert 1 + 1 == 2
def test_resta():
assert 3 - 1 == 2
Enfoque de clases
# test_calculadora.py
class TestCalculadora:
def test_suma(self):
assert 1 + 1 == 2
def test_resta(self):
assert 3 - 1 == 2
El enfoque de clases es particularmente útil cuando necesitas compartir estado o configuración entre varios tests.
Estructura de directorios recomendada
Para proyectos de tamaño mediano a grande, es recomendable seguir una estructura de directorios que separe claramente el código de producción de los tests:
mi_proyecto/
├── src/
│ └── mi_paquete/
│ ├── __init__.py
│ └── calculadora.py
└── tests/
├── __init__.py
├── unit/
│ └── test_calculadora.py
└── integration/
└── test_sistema.py
Esta estructura permite organizar los tests por tipo (unitarios, integración, etc.) y mantiene una clara separación entre código de producción y código de prueba.
Convenciones de nomenclatura
Seguir convenciones de nomenclatura consistentes facilita la organización y descubrimiento de tests:
- Archivos de test:
test_<modulo>.py
o<modulo>_test.py
- Funciones de test:
test_<funcionalidad>()
- Clases de test:
Test<Componente>
- Métodos de test:
test_<funcionalidad>()
Escribiendo tests efectivos
Un buen test debe ser claro, conciso y enfocado en probar una única funcionalidad. El patrón AAA (Arrange-Act-Assert) es una buena práctica para estructurar tus tests:
def test_division():
# Arrange (Preparar)
a = 10
b = 2
# Act (Actuar)
resultado = a / b
# Assert (Verificar)
assert resultado == 5
Este patrón hace que los tests sean más legibles y mantenibles al separar claramente la preparación, la acción y la verificación.
Testing de excepciones
Verificar que el código lanza las excepciones esperadas es una parte importante del testing. Pytest proporciona una forma elegante de probar excepciones:
import pytest
def dividir(a, b):
if b == 0:
raise ValueError("No se puede dividir por cero")
return a / b
def test_division_por_cero():
with pytest.raises(ValueError) as excinfo:
dividir(10, 0)
# Opcionalmente, verificar el mensaje de error
assert "No se puede dividir por cero" in str(excinfo.value)
El contexto pytest.raises()
captura la excepción y permite verificar tanto su tipo como su mensaje.
Parametrización de tests
La parametrización permite ejecutar el mismo test con diferentes conjuntos de datos, lo que reduce la duplicación de código y aumenta la cobertura:
import pytest
def es_par(numero):
return numero % 2 == 0
@pytest.mark.parametrize("numero,esperado", [
(2, True),
(3, False),
(4, True),
(5, False),
])
def test_es_par(numero, esperado):
assert es_par(numero) == esperado
El decorador @pytest.mark.parametrize
recibe dos argumentos:
- Una cadena con los nombres de los parámetros separados por comas
- Una lista de tuplas con los valores para cada combinación de parámetros
Testing de funciones con efectos secundarios
Cuando probamos funciones que tienen efectos secundarios (como modificar archivos o bases de datos), es importante asegurarse de que el test no afecte al entorno real:
import os
import tempfile
def test_escritura_archivo():
# Crear un archivo temporal
fd, path = tempfile.mkstemp()
try:
with os.fdopen(fd, 'w') as temp:
temp.write("datos de prueba")
# Verificar que el archivo contiene lo esperado
with open(path, 'r') as f:
contenido = f.read()
assert contenido == "datos de prueba"
finally:
# Limpiar siempre, incluso si el test falla
os.unlink(path)
Este enfoque garantiza que el test sea aislado y no deje "residuos" en el sistema.
Testing de código asíncrono
Python 3.13 mejora significativamente el soporte para código asíncrono. Pytest permite probar funciones asíncronas de forma natural:
import asyncio
import pytest
async def obtener_datos():
await asyncio.sleep(0.1) # Simular operación asíncrona
return {"status": "ok", "data": [1, 2, 3]}
@pytest.mark.asyncio
async def test_obtener_datos():
resultado = await obtener_datos()
assert resultado["status"] == "ok"
assert len(resultado["data"]) == 3
El decorador @pytest.mark.asyncio
indica a pytest que debe ejecutar la función de test en un bucle de eventos asyncio. Para usar este decorador, necesitas instalar el plugin pytest-asyncio
:
pip install pytest-asyncio
Testing de aplicaciones web
Para aplicaciones web, puedes combinar pytest con bibliotecas como requests
para tests de integración:
import requests
def test_api_endpoint(servidor_prueba):
# servidor_prueba sería una fixture que proporciona la URL base
url = f"{servidor_prueba}/api/usuarios"
respuesta = requests.get(url)
assert respuesta.status_code == 200
datos = respuesta.json()
assert "usuarios" in datos
Buenas prácticas para escribir tests
- Independencia: Cada test debe poder ejecutarse de forma aislada
- Determinismo: Los tests deben producir el mismo resultado en cada ejecución
- Enfoque: Cada test debe verificar una única funcionalidad
- Claridad: El propósito del test debe ser evidente por su nombre y estructura
- Rapidez: Los tests deben ejecutarse rápidamente para facilitar el desarrollo iterativo
Docstrings en tests
Documentar tus tests con docstrings claros ayuda a entender su propósito y comportamiento:
def test_autenticacion_usuario():
"""
Verifica que un usuario válido puede autenticarse correctamente.
El test comprueba:
1. Que se devuelve un token JWT válido
2. Que el token contiene la información correcta del usuario
"""
# Implementación del test
Tests que documentan el código
Los tests bien escritos sirven como documentación viva del código. Muestran cómo se espera que funcione cada componente y proporcionan ejemplos de uso:
def test_formato_moneda():
"""Demuestra el uso de la función formato_moneda."""
# Este test muestra que la función:
# 1. Añade el símbolo de moneda
# 2. Usa separadores de miles
# 3. Muestra dos decimales
assert formato_moneda(1234.56, "€") == "€1.234,56"
assert formato_moneda(1000, "$") == "$1.000,00"
Marcadores personalizados
Los marcadores permiten categorizar tus tests y ejecutarlos selectivamente:
import pytest
@pytest.mark.lento
def test_operacion_costosa():
# Test que toma mucho tiempo
pass
@pytest.mark.integracion
def test_conexion_bd():
# Test que requiere una base de datos
pass
Para registrar estos marcadores y evitar advertencias, añade en tu pyproject.toml
:
[tool.pytest.ini_options]
markers = [
"lento: tests que toman mucho tiempo",
"integracion: tests que requieren recursos externos",
]
Luego puedes ejecutar tests específicos:
# Ejecutar solo tests marcados como 'integracion'
pytest -m integracion
# Ejecutar todos excepto los tests lentos
pytest -m "not lento"
Testing de código con dependencias externas
Para probar código que depende de servicios externos, es recomendable usar mocks o stubs que simulen estas dependencias:
from unittest.mock import patch
def obtener_clima(ciudad):
# En producción, esta función llamaría a una API externa
# ...
pass
def test_obtener_clima():
# Simular la respuesta de la API externa
with patch('mi_modulo.requests.get') as mock_get:
mock_get.return_value.json.return_value = {
"temperatura": 25,
"condicion": "soleado"
}
mock_get.return_value.status_code = 200
resultado = obtener_clima("Madrid")
assert resultado["temperatura"] == 25
assert resultado["condicion"] == "soleado"
Este enfoque permite probar tu código sin depender de servicios externos, lo que hace que los tests sean más rápidos y confiables.
Aserciones y fixtures
Las aserciones y fixtures son dos componentes fundamentales en pytest que permiten crear tests robustos, mantenibles y expresivos. Mientras que las aserciones verifican que el código se comporta como esperamos, las fixtures proporcionan un mecanismo elegante para preparar el entorno de prueba y compartir recursos entre tests.
Aserciones en pytest
A diferencia de otros frameworks de testing que requieren métodos específicos para realizar comprobaciones, pytest aprovecha la instrucción assert
nativa de Python y la mejora con mensajes de error detallados y contextuales.
Aserciones básicas
La forma más simple de verificar resultados es mediante la instrucción assert
:
def test_suma_simple():
resultado = 2 + 2
assert resultado == 4
Cuando una aserción falla, pytest genera automáticamente un mensaje de error detallado que muestra los valores reales y esperados:
def test_ejemplo_fallo():
lista = [1, 2, 3, 5]
assert 4 in lista # Esto fallará
Al ejecutar este test, pytest mostrará algo como:
E assert 4 in [1, 2, 3, 5]
E + where [1, 2, 3, 5] = [1, 2, 3, 5]
Comparación de estructuras complejas
Pytest maneja elegantemente la comparación de estructuras de datos complejas como diccionarios y listas:
def test_diccionario():
resultado = {"nombre": "Ana", "edad": 30, "roles": ["admin", "editor"]}
esperado = {"nombre": "Ana", "edad": 30, "roles": ["admin", "editor"]}
assert resultado == esperado
Si hay diferencias, pytest mostrará exactamente dónde están:
def test_diccionario_diferencia():
resultado = {"nombre": "Ana", "edad": 30, "roles": ["admin", "editor"]}
esperado = {"nombre": "Ana", "edad": 31, "roles": ["admin", "editor"]}
assert resultado == esperado # Fallará y mostrará la diferencia en "edad"
Aserciones con mensajes personalizados
Puedes añadir mensajes personalizados a tus aserciones para proporcionar contexto adicional:
def test_con_mensaje():
usuario_id = obtener_id_usuario("admin@ejemplo.com")
assert usuario_id is not None, "No se pudo encontrar el ID para admin@ejemplo.com"
Verificación de excepciones
Para verificar que una función lanza la excepción esperada, utiliza el contexto pytest.raises
:
import pytest
def dividir(a, b):
if b == 0:
raise ValueError("División por cero")
return a / b
def test_division_por_cero():
with pytest.raises(ValueError) as excinfo:
dividir(10, 0)
# Verificar el mensaje de la excepción
assert "División por cero" in str(excinfo.value)
También puedes usar una sintaxis más concisa cuando solo necesitas verificar el tipo de excepción:
def test_division_por_cero_simple():
with pytest.raises(ValueError):
dividir(10, 0)
Verificación de advertencias
De manera similar, puedes verificar que tu código emite advertencias específicas:
import warnings
def funcion_con_advertencia():
warnings.warn("Esta función está obsoleta", DeprecationWarning)
return True
def test_advertencia():
with pytest.warns(DeprecationWarning):
assert funcion_con_advertencia()
Aserciones aproximadas
Para comparaciones de números de punto flotante, donde la igualdad exacta puede ser problemática debido a errores de redondeo, pytest proporciona pytest.approx
:
def test_aproximacion():
resultado = 0.1 + 0.2
assert resultado == pytest.approx(0.3) # Pasa a pesar de que 0.1 + 0.2 != 0.3 exactamente
También puedes especificar la tolerancia:
def test_aproximacion_con_tolerancia():
valor = calcular_pi()
assert valor == pytest.approx(3.14159, abs=1e-5)
Fixtures: preparando el entorno de prueba
Las fixtures son funciones que pytest ejecuta antes (y opcionalmente después) de los tests para preparar el entorno de prueba. Proporcionan una forma elegante de:
- Configurar precondiciones para los tests
- Proporcionar datos de prueba
- Inyectar dependencias
- Gestionar recursos compartidos
- Realizar limpieza después de los tests
Definición básica de fixtures
Una fixture se define como una función decorada con @pytest.fixture
:
import pytest
@pytest.fixture
def usuario_prueba():
return {"id": 1, "nombre": "Usuario Test", "email": "test@ejemplo.com"}
def test_nombre_usuario(usuario_prueba):
assert usuario_prueba["nombre"] == "Usuario Test"
Cuando un test recibe un parámetro con el mismo nombre que una fixture, pytest automáticamente ejecuta la fixture y pasa su valor de retorno al test.
Alcance de las fixtures
El alcance (scope) de una fixture determina con qué frecuencia se ejecuta:
@pytest.fixture(scope="function") # Por defecto, se ejecuta para cada test
def conexion_bd_efimera():
# Configurar
conexion = crear_conexion_bd_prueba()
yield conexion
# Limpiar
conexion.cerrar()
@pytest.fixture(scope="module") # Se ejecuta una vez por módulo
def datos_compartidos():
return cargar_datos_prueba()
@pytest.fixture(scope="session") # Se ejecuta una vez por sesión de pytest
def servidor_prueba():
# Iniciar servidor
servidor = iniciar_servidor_prueba()
yield servidor
# Detener servidor
servidor.detener()
Los posibles valores para scope
son:
- function: Se ejecuta para cada test (valor predeterminado)
- class: Se ejecuta una vez por clase de test
- module: Se ejecuta una vez por módulo
- package: Se ejecuta una vez por paquete
- session: Se ejecuta una vez por sesión de pytest
Fixtures con finalización
Para realizar limpieza después de un test, utiliza yield
en lugar de return
:
import os
import tempfile
@pytest.fixture
def archivo_temporal():
# Configuración: crear archivo temporal
fd, ruta = tempfile.mkstemp()
with os.fdopen(fd, 'w') as f:
f.write("datos iniciales")
# Proporcionar la ruta al test
yield ruta
# Limpieza: eliminar archivo temporal
os.unlink(ruta)
def test_leer_archivo(archivo_temporal):
with open(archivo_temporal, 'r') as f:
contenido = f.read()
assert contenido == "datos iniciales"
# Modificar el archivo
with open(archivo_temporal, 'w') as f:
f.write("nuevos datos")
# El archivo se eliminará automáticamente después del test
Fixtures parametrizadas
Las fixtures pueden ser parametrizadas para proporcionar múltiples valores:
@pytest.fixture(params=[10, 20, 30])
def valor_entrada(request):
return request.param
def test_multiplicacion(valor_entrada):
resultado = valor_entrada * 2
assert resultado == valor_entrada * 2
Este test se ejecutará tres veces, una para cada valor del parámetro.
Fixtures que utilizan otras fixtures
Las fixtures pueden depender de otras fixtures:
@pytest.fixture
def usuario():
return {"id": 1, "nombre": "Admin"}
@pytest.fixture
def usuario_autenticado(usuario):
usuario["token"] = "jwt_token_simulado"
return usuario
def test_usuario_tiene_token(usuario_autenticado):
assert "token" in usuario_autenticado
assert usuario_autenticado["token"] == "jwt_token_simulado"
Fixtures autousables
Las fixtures marcadas con autouse=True
se ejecutan automáticamente para cada test, sin necesidad de declararlas como parámetros:
@pytest.fixture(autouse=True)
def configurar_entorno():
# Configurar variables de entorno para los tests
os.environ["ENTORNO"] = "prueba"
yield
# Restaurar
os.environ.pop("ENTORNO", None)
Compartir fixtures entre módulos con conftest.py
Para compartir fixtures entre múltiples archivos de test, colócalas en un archivo llamado conftest.py
:
# conftest.py
import pytest
import sqlite3
@pytest.fixture(scope="session")
def bd_prueba():
# Crear BD en memoria
conexion = sqlite3.connect(":memory:")
cursor = conexion.cursor()
# Crear esquema
cursor.execute("""
CREATE TABLE usuarios (
id INTEGER PRIMARY KEY,
nombre TEXT,
email TEXT
)
""")
# Insertar datos de prueba
cursor.execute("""
INSERT INTO usuarios (nombre, email)
VALUES ('Usuario Test', 'test@ejemplo.com')
""")
conexion.commit()
yield conexion
# Cerrar conexión
conexion.close()
Luego, cualquier test en el mismo directorio o subdirectorios puede usar esta fixture:
# test_usuarios.py
def test_consulta_usuario(bd_prueba):
cursor = bd_prueba.cursor()
cursor.execute("SELECT * FROM usuarios WHERE nombre = ?", ("Usuario Test",))
usuario = cursor.fetchone()
assert usuario is not None
assert usuario[1] == "Usuario Test"
Fixtures dinámicas
Puedes crear fixtures dinámicamente utilizando la función request.getfixturevalue()
:
@pytest.fixture
def datos_usuario():
return {"nombre": "Ana", "rol": "admin"}
@pytest.fixture
def permisos_usuario(request):
datos = request.getfixturevalue("datos_usuario")
if datos["rol"] == "admin":
return ["leer", "escribir", "eliminar"]
else:
return ["leer"]
def test_permisos_admin(permisos_usuario):
assert "eliminar" in permisos_usuario
Fixtures para mocks
Las fixtures son ideales para configurar mocks que simulan dependencias externas:
from unittest.mock import patch, MagicMock
@pytest.fixture
def mock_api_clima():
with patch('mi_app.servicios.api_clima.obtener_temperatura') as mock:
mock.return_value = 25.5
yield mock
def test_recomendacion_clima(mock_api_clima):
from mi_app.recomendaciones import recomendar_actividad
actividad = recomendar_actividad("Madrid")
assert "soleado" in actividad.lower()
# Verificar que se llamó a la API con los parámetros correctos
mock_api_clima.assert_called_once_with("Madrid")
Fixtures para pruebas asíncronas
Para tests asíncronos, puedes crear fixtures que devuelvan objetos awaitable:
import pytest
import asyncio
@pytest.fixture
async def datos_async():
await asyncio.sleep(0.1) # Simular operación asíncrona
return {"status": "ok", "datos": [1, 2, 3]}
@pytest.mark.asyncio
async def test_procesamiento_asincrono(datos_async):
resultado = await procesar_datos(datos_async)
assert resultado["procesado"] == True
Para usar fixtures asíncronas, necesitas instalar el plugin pytest-asyncio
.
Combinando fixtures y parametrización
Puedes combinar fixtures con la parametrización de tests para crear casos de prueba más completos:
@pytest.fixture
def cliente_api():
return ClienteAPI(base_url="https://api.ejemplo.com")
@pytest.mark.parametrize("id_usuario,nombre_esperado", [
(1, "Ana García"),
(2, "Carlos López"),
(3, "Elena Martínez")
])
def test_obtener_usuario(cliente_api, id_usuario, nombre_esperado):
# Configurar mock para simular respuesta de API
cliente_api.configurar_respuesta(f"/usuarios/{id_usuario}", {
"id": id_usuario,
"nombre": nombre_esperado
})
# Ejecutar función bajo prueba
usuario = obtener_detalles_usuario(cliente_api, id_usuario)
# Verificar resultado
assert usuario["nombre"] == nombre_esperado
Mejores prácticas para aserciones y fixtures
- Mantén las fixtures enfocadas: Cada fixture debe tener una responsabilidad clara.
- Usa el alcance adecuado: Elige el scope más restrictivo posible para mejorar el rendimiento.
- Nombra descriptivamente: Usa nombres claros que indiquen lo que proporciona la fixture.
- Documenta tus fixtures: Añade docstrings que expliquen el propósito y uso de cada fixture.
- Prefiere múltiples fixtures pequeñas: En lugar de una fixture grande que lo haga todo.
- Limpia siempre los recursos: Usa
yield
para asegurar la limpieza de recursos como archivos o conexiones. - Evita efectos secundarios: Las fixtures no deben modificar el estado global de forma permanente.
Con estas herramientas, puedes crear tests que no solo verifican el comportamiento de tu código, sino que también son legibles, mantenibles y eficientes.
Ejercicios de esta lección Testing con pytest
Evalúa tus conocimientos de esta lección Testing con pytest con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.
Módulo math
Reto herencia
Excepciones
Introducción a Python
Reto variables
Funciones Python
Reto funciones
Módulo datetime
Reto acumulación
Reto estructuras condicionales
Polimorfismo
Módulo os
Reto métodos dunder
Diccionarios
Reto clases y objetos
Reto operadores
Operadores
Estructuras de control
Funciones lambda
Reto diccionarios
Reto función lambda
Encapsulación
Reto coleciones
Reto funciones auxiliares
Crear módulos y paquetes
Módulo datetime
Excepciones
Operadores
Diccionarios
Reto map, filter
Reto tuplas
Proyecto gestor de tareas CRUD
Tuplas
Variables
Tipos de datos
Conjuntos
Reto mixins
Módulo csv
Módulo json
Herencia
Análisis de datos de ventas con Pandas
Reto fechas y tiempo
Reto estructuras de iteración
Funciones
Reto comprehensions
Variables
Reto serialización
Módulo csv
Reto polimorfismo
Polimorfismo
Clases y objetos
Reto encapsulación
Estructuras de control
Importar módulos y paquetes
Módulo math
Funciones lambda
Reto excepciones
Listas
Reto archivos
Encapsulación
Reto conjuntos
Clases y objetos
Instalación de Python y creación de proyecto
Reto listas
Tipos de datos
Crear módulos y paquetes
Tuplas
Herencia
Reto acceso a sistema
Proyecto sintaxis calculadora
Importar módulos y paquetes
Clases y objetos
Módulo os
Listas
Conjuntos
Reto tipos de datos
Reto matemáticas
Módulo json
Todas las lecciones de Python
Accede a todas las lecciones de Python y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.
Introducción A Python
Introducción
Instalación Y Creación De Proyecto
Introducción
Tema 2: Tipos De Datos, Variables Y Operadores
Introducción
Instalación De Python
Introducción
Tipos De Datos
Sintaxis
Variables
Sintaxis
Operadores
Sintaxis
Estructuras De Control
Sintaxis
Funciones
Sintaxis
Estructuras Control Iterativo
Sintaxis
Estructuras Control Condicional
Sintaxis
Testing Con Pytest
Sintaxis
Listas
Estructuras De Datos
Tuplas
Estructuras De Datos
Diccionarios
Estructuras De Datos
Conjuntos
Estructuras De Datos
Comprehensions
Estructuras De Datos
Clases Y Objetos
Programación Orientada A Objetos
Excepciones
Programación Orientada A Objetos
Encapsulación
Programación Orientada A Objetos
Herencia
Programación Orientada A Objetos
Polimorfismo
Programación Orientada A Objetos
Mixins Y Herencia Múltiple
Programación Orientada A Objetos
Métodos Especiales (Dunder Methods)
Programación Orientada A Objetos
Composición De Clases
Programación Orientada A Objetos
Funciones Lambda
Programación Funcional
Aplicación Parcial
Programación Funcional
Entrada Y Salida, Manejo De Archivos
Programación Funcional
Decoradores
Programación Funcional
Generadores
Programación Funcional
Paradigma Funcional
Programación Funcional
Composición De Funciones
Programación Funcional
Funciones Orden Superior Map Y Filter
Programación Funcional
Funciones Auxiliares
Programación Funcional
Reducción Y Acumulación
Programación Funcional
Archivos Comprimidos
Entrada Y Salida Io
Entrada Y Salida Avanzada
Entrada Y Salida Io
Archivos Temporales
Entrada Y Salida Io
Contexto With
Entrada Y Salida Io
Módulo Csv
Biblioteca Estándar
Módulo Json
Biblioteca Estándar
Módulo Datetime
Biblioteca Estándar
Módulo Math
Biblioteca Estándar
Módulo Os
Biblioteca Estándar
Módulo Re
Biblioteca Estándar
Módulo Random
Biblioteca Estándar
Módulo Time
Biblioteca Estándar
Módulo Collections
Biblioteca Estándar
Módulo Sys
Biblioteca Estándar
Módulo Statistics
Biblioteca Estándar
Módulo Pickle
Biblioteca Estándar
Módulo Pathlib
Biblioteca Estándar
Importar Módulos Y Paquetes
Paquetes Y Módulos
Crear Módulos Y Paquetes
Paquetes Y Módulos
Entornos Virtuales (Virtualenv, Venv)
Entorno Y Dependencias
Gestión De Dependencias (Pip, Requirements.txt)
Entorno Y Dependencias
Python-dotenv Y Variables De Entorno
Entorno Y Dependencias
Acceso A Datos Con Mysql, Pymongo Y Pandas
Acceso A Bases De Datos
Acceso A Mongodb Con Pymongo
Acceso A Bases De Datos
Acceso A Mysql Con Mysql Connector
Acceso A Bases De Datos
Novedades Python 3.13
Características Modernas
Operador Walrus
Características Modernas
Pattern Matching
Características Modernas
Instalación Beautiful Soup
Web Scraping
Sintaxis General De Beautiful Soup
Web Scraping
Tipos De Selectores
Web Scraping
Web Scraping De Html
Web Scraping
Web Scraping Para Ciencia De Datos
Web Scraping
Autenticación Y Acceso A Recursos Protegidos
Web Scraping
Combinación De Selenium Con Beautiful Soup
Web Scraping
En esta lección
Objetivos de aprendizaje de esta lección
- Instalar y configurar pytest y sus plugins en entornos virtuales.
- Escribir y organizar tests efectivos usando funciones y clases.
- Utilizar aserciones avanzadas para verificar comportamientos y excepciones.
- Crear y gestionar fixtures para preparar y limpiar el entorno de pruebas.
- Aplicar buenas prácticas y técnicas avanzadas como parametrización y testing asíncrono.