tutoriales.com

Crea tus Propias Pruebas de Software con Pytest: Guía Práctica para Desarrolladores Python

Descubre cómo elevar la calidad de tu código Python utilizando Pytest, el framework de pruebas más popular y potente de la actualidad. Aprenderás desde la instalación básica hasta técnicas avanzadas como fixtures y parametrización.

Intermedio8 min de lectura4 views
Reportar error

Introducción al Mundo de las Pruebas con Pytest 🚀

Escribir código funcional es solo la mitad del trabajo en el desarrollo de software moderno. La otra mitad, igualmente crucial, es asegurarnos de que ese código siga funcionando correctamente a medida que crece, evoluciona y recibe contribuciones de otros desarrolladores. Aquí es donde entran en juego las pruebas automatizadas. Intermedio

Entre las múltiples herramientas disponibles en el ecosistema de Python, Pytest se ha consolidado como el estándar de la industria gracias a su sintaxis limpia, su potente sistema de aserciones y su gigantesco ecosistema de plugins. A diferencia del módulo incorporado unittest, Pytest nos permite escribir pruebas con menos código boilerplate y aprovechar características avanzadas de manera natural.

💡 Consejo: Si vienes de usar `unittest`, olvídate de las clases obligatorias y los métodos `self.assertEqual()`. Con Pytest, una simple función que comience con el prefijo `test_` es suficiente para empezar.

En este tutorial completo, aprenderás paso a paso cómo estructurar, escribir y ejecutar pruebas profesionales en tus proyectos de Python utilizando Pytest.


Instalación y Configuración del Entorno 🛠️

Antes de escribir nuestra primera prueba, necesitamos configurar nuestro entorno de desarrollo. Asegúrate de tener Python instalado en tu sistema (recomendamos la versión 3.8 o superior).

Para mantener nuestro proyecto limpio, crearemos un entorno virtual e instalaremos Pytest en él. Abre tu terminal y ejecuta los siguientes comandos:

python -m venv venv
source venv/bin/activate  # En Windows usa: venv\Scripts\activate
pip install pytest

Una vez completada la instalación, puedes verificar que todo funciona correctamente ejecutando:

pytest --version

Estructura de un Proyecto de Pruebas

Una buena organización del código es fundamental para el mantenimiento a largo plazo. La estructura recomendada para proyectos pequeños y medianos con Pytest suele verse así:

mi_proyecto/
│
├── src/
│   ├── __init__.py
│   └── calculadora.py
│
└── tests/
    ├── __init__.py
    └── test_calculadora.py
mi-proyecto/ src/ (Producción) tests/ (Pruebas) components/ services/ index.js unit/ integration/ app.test.js Separación de Responsabilidades: Lógica vs Verificación

Escribiendo Nuestra Primera Prueba en Pytest ✨

Vamos a crear un archivo simple en src/calculadora.py que contendrá funciones matemáticas básicas para probarlas posteriormente.

def sumar(a, b):
    return a + b

def dividir(a, b):
    if b == 0:
        raise ValueError("No se puede dividir por cero")
    return a / b

Ahora, crearemos el archivo de pruebas correspondiente en tests/test_calculadora.py. Aquí es donde aplicaremos las aserciones nativas de Python combinadas con la potencia de Pytest.

from src.calculadora import sumar, dividir
import pytest

def test_sumar():
    resultado = sumar(3, 5)
    assert resultado == 8

def test_sumar_negativos():
    assert sumar(-2, -3) == -5

def test_dividir():
    assert dividir(10, 2) == 5.0

def test_dividir_por_cero():
    with pytest.raises(ValueError):
        dividir(10, 0)

¿Cómo ejecutar las pruebas?

Para ejecutar todas las pruebas que hemos creado, simplemente dirígete a la raíz de tu proyecto en la terminal y escribe:

pytest

Pytest buscará automáticamente todos los archivos que comiencen o terminen con test_ o _test y ejecutará las funciones que cumplan con el mismo patrón.


Dominando las Fixtures de Pytest 💡

Las fixtures son una de las características más potentes de Pytest. Nos permiten configurar un estado inicial predecible para nuestras pruebas (como conexiones a bases de datos, creación de archivos temporales o instancias de objetos complejos) y limpiar los recursos después de que la prueba finaliza.

Imagina que necesitamos probar una clase de gestión de usuarios que requiere una base de datos temporal en memoria. Veamos cómo usar fixtures para esto:

import pytest

class BaseDeDatosFalsa:
    def __init__(self):
        self.datos = {}

    def guardar(self, clave, valor):
        self.datos[clave] = valor

    def obtener(self, clave):
        self.datos.get(clave)

@pytest.fixture
py_db():
    # Configuración previa a la prueba
    db = BaseDeDatosFalsa()
    db.guardar("admin", "secreto123")
    yield db
    # Código de limpieza (opcional si se requiere)
    db.datos.clear()

def test_usuario_existe(py_db):
    assert py_db.datos.get("admin") == "secreto123"

def test_agregar_usuario(py_db):
    py_db.guardar("invitado", "guest456")
    assert py_db.datos.get("invitado") == "guest456"
📌 Nota: La palabra clave `yield` dentro de una fixture actúa como separador. Todo el código anterior al `yield` se ejecuta antes de la prueba, y todo el código posterior se ejecuta como limpieza al finalizar.

Parametrización: Escribe Menos Código y Prueba Más 🔄

A menudo nos encontramos escribiendo múltiples pruebas que hacen exactamente lo mismo, pero con diferentes entradas y salidas esperadas. En lugar de duplicar código, Pytest nos ofrece el decorador @pytest.mark.parametrize.

Analicemos el siguiente ejemplo para probar una función que verifica si una palabra es un palíndromo:

def es_palindromo(palabra):
    limpio = palabra.lower().replace(" ", "")
    return limpio == limpio[::-1]

import pytest

@pytest.mark.parametrize("entrada, esperado", [
    ("anita lava la tina", True),
    ("radar", True),
    ("python", False),
    ("reconocer", True),
    ("desarrollo", False)
])
def test_es_palindromo(entrada, esperado):
    assert es_palindromo(entrada) == esperado
@pytest.mark.parametrize Dataset A (input_1, expected_1) Dataset B (input_2, expected_2) Dataset C (input_3, expected_3) Ejecución 1 test_func(A) Ejecución 2 test_func(B) Ejecución 3 test_func(C) Ejecución en paralelo (x-dist)

Gracias a la parametrización, Pytest ejecutará esta prueba 5 veces de forma independiente, reportando el éxito o fallo de cada caso de prueba por separado.


Buenas Prácticas y Manejo de Excepciones ⚠️

Para mantener una suite de pruebas robusta y profesional, es recomendable seguir una serie de pautas que garantizan la legibilidad y el rendimiento:

Buena PrácticaDescripciónBeneficio
---------
IndependenciaLas pruebas nunca deben depender del resultado de otras.Facilita la depuración y evita efectos colaterales.
Nombres DescriptivosNombra tus funciones de prueba indicando claramente qué comportamiento verifican.Permite entender qué falló con solo leer el reporte de la terminal.
---------
Una Aserción PrincipalAunque se permiten múltiples asserts, enfoca cada test en validar una sola regla de negocio.Aislar fallos de manera precisa.

Probando Excepciones Personalizadas

Cuando desarrollamos lógica de negocio, es vital comprobar que nuestro código lanza las excepciones correctas ante entradas erróneas. Ya vimos un ejemplo básico con pytest.raises, pero podemos extraer información detallada de la excepción:

def validar_edad(edad):
    if edad < 0:
        raise ValueError("La edad no puede ser negativa")
    if edad > 120:
        raise ValueError("Edad fuera del rango realista")
    return True

def test_validar_edad_negativa():
    with pytest.raises(ValueError) as exc_info:
        validar_edad(-5)
    assert "La edad no puede ser negativa" in str(exc_info.value)

Resumen del Progreso de Aprendizaje

Has recorrido un camino excelente para dominar las bases y técnicas intermedias de Pytest. Repasemos tu avance actual:

90% Completado
Paso 1: Instalación y configuración inicial del entorno de pruebas.
Paso 2: Creación de funciones de prueba básicas y uso de aserciones.
Paso 3: Implementación de Fixtures para gestionar estados y dependencias.
Paso 4: Uso avanzado de Parametrización para múltiples casos de prueba.

Preguntas Frecuentes (FAQ)

¿Puedo usar Pytest junto con unittest de Python? Sí, Pytest es completamente compatible con las clases de prueba heredadas de `unittest.TestCase`. Puedes ejecutar tus pruebas antiguas de unittest directamente usando el comando `pytest` en la terminal.
¿Cómo genero un reporte de cobertura de código? Debes instalar el paquete adicional `pytest-cov` mediante pip (`pip install pytest-cov`) y ejecutar tus pruebas con el comando `pytest --cov=src`.

Conclusión

Las pruebas automatizadas ya no son un lujo opcional en el desarrollo de software moderno, sino una necesidad absoluta para garantizar calidad, estabilidad y escalabilidad. Con Pytest, cuentas con una herramienta versátil, potente y sumamente elegante que se adapta tanto a proyectos pequeños como a arquitecturas empresariales complejas. ¡Es momento de aplicar lo aprendido en tu próximo repositorio de Python!

Tutoriales relacionados

Comentarios (0)

Aún no hay comentarios. ¡Sé el primero!