Un test es código que verifica tu código. pytest es el framework estándar de Python: escribes funciones normales con asserts y él hace el resto. Sin tests, cada cambio es un salto al vacío.
Tu primer test
Digamos que tienes mis_funciones.py:
def sumar(a, b):
return a + b
Creas test_mis_funciones.py:
from mis_funciones import sumar
def test_sumar_dos_numeros():
assert sumar(2, 3) == 5
def test_sumar_negativos():
assert sumar(-1, -1) == -2
Ejecutas en la terminal:
pytest # descubre y corre todos los test_*.py
Y ves:
test_mis_funciones.py .. [100%]
2 passed
Eso es todo: archivos test_*.py con funciones test_*() que usan assert. pytest los descubre por convención.
La convención de descubrimiento
- Archivos:
test_algo.pyoalgo_test.py - Funciones: empiezan con
test_ - Clases de tests:
TestAlgo(sin__init__)
Si el nombre no cumple la convención, pytest no lo corre — error típico de principiante.
Qué probar: los tres niveles
def dividir(a, b):
if b == 0:
raise ValueError("división entre cero")
return a / b
def test_caso_normal():
assert dividir(10, 2) == 5.0
def test_caso_limite():
assert dividir(0, 5) == 0.0 # el "borde" de tu dominio
def test_caso_de_error(): # verificar que SÍ lanza
import pytest
with pytest.raises(ValueError):
dividir(1, 0)
- Caso normal: entrada típica, resultado esperado
- Casos límite: ceros, vacíos, negativos, valores máximos — donde viven los bugs
- Casos de error: verificar que lanza la excepción correcta (
pytest.raises)
Fixtures: preparar el escenario
Cuando varios tests necesitan los mismos datos, una fixture los construye:
import pytest
@pytest.fixture
def lista_ejemplo():
return [3, 1, 2]
def test_maximo(lista_ejemplo): # pytest inyecta la fixture
assert max(lista_ejemplo) == 3
def test_ordenada(lista_ejemplo):
assert sorted(lista_ejemplo) == [1, 2, 3]
La fixture se declara una vez y pytest la pasa como argumento por su nombre. Ideal para datos de prueba, conexiones temporales, archivos temporales (tmp_path).
Estructura de un buen test: AAA
def test_retiro_resta_del_saldo():
# Arrange: preparar el escenario
cuenta = Cuenta(saldo_inicial=100)
# Act: ejecutar la acción bajo prueba
cuenta.retirar(30)
# Assert: verificar el resultado
assert cuenta.saldo == 70
Un test = una sola razón de fallo. Si pruebas cinco cosas en un test y falla, no sabes cuál.
La regla de oro
No pruebes la implementación (cómo lo hace); prueba el comportamiento (qué promete). Así puedes refactorizar todo el interior sin romper un solo test — la red de seguridad que te deja moverte rápido sin miedo.
Resumen
test_*.pycon funcionestest_*()yassert— eso es pytest- Prueba casos normales, límites y errores (
pytest.raises) - Las fixtures preparan datos reutilizables que se inyectan por nombre
- Estructura AAA: Arrange, Act, Assert
- Los tests no evitan bugs: los detectan antes de que lleguen a producción