En este tema construiremos un proyecto Python pequeño, pero con suficiente variedad para practicar testing durante los próximos temas. Tendrá funciones simples, validaciones, excepciones, una clase con estado y un archivo preparado para ejecutar algunas comprobaciones manuales.
La idea no es crear una aplicación grande. La idea es tener código concreto para probar. Si el código de ejemplo es claro, las pruebas también serán más fáciles de entender.
Desde la carpeta general del curso, crea un nuevo proyecto:
mkdir tienda-demo
cd tienda-demo
Este proyecto será independiente de los ejemplos anteriores. Así podremos practicar sin mezclar archivos de distintos temas.
Crea el entorno virtual:
python -m venv .venv
Actívalo en Windows PowerShell:
.venv\Scripts\Activate.ps1
En Linux o macOS:
source .venv/bin/activate
Actualiza pip e instala pytest:
python -m pip install --upgrade pip
python -m pip install pytest
Para este proyecto usaremos una estructura un poco más ordenada que en los primeros ejemplos:
mkdir tienda
mkdir tests
Luego crea un archivo vacío llamado __init__.py dentro de la carpeta tienda. En Windows PowerShell:
New-Item tienda\__init__.py -ItemType File
En Linux o macOS:
touch tienda/__init__.py
Ese archivo indica que tienda puede usarse como paquete Python.
Por ahora la carpeta debería verse así:
tienda-demo/
|-- .venv/
|-- tienda/
| `-- __init__.py
`-- tests/
El código principal irá dentro de tienda y las pruebas irán dentro de tests.
Dentro de la carpeta tienda, crea el archivo precios.py:
def aplicar_descuento(precio, porcentaje):
if precio < 0:
raise ValueError("El precio no puede ser negativo")
if porcentaje < 0 or porcentaje > 100:
raise ValueError("El porcentaje debe estar entre 0 y 100")
return precio - (precio * porcentaje / 100)
def calcular_impuesto(precio, porcentaje_impuesto):
if precio < 0:
raise ValueError("El precio no puede ser negativo")
if porcentaje_impuesto < 0:
raise ValueError("El impuesto no puede ser negativo")
return precio * porcentaje_impuesto / 100
def calcular_total(precio, descuento, impuesto):
precio_con_descuento = aplicar_descuento(precio, descuento)
importe_impuesto = calcular_impuesto(precio_con_descuento, impuesto)
return precio_con_descuento + importe_impuesto
Este módulo tiene varios comportamientos interesantes para probar: cálculos correctos, datos inválidos y uso de funciones auxiliares.
Dentro de tienda, crea productos.py:
def normalizar_nombre(nombre):
return nombre.strip().title()
def validar_producto(nombre, precio):
if not nombre or not nombre.strip():
raise ValueError("El nombre del producto es obligatorio")
if precio <= 0:
raise ValueError("El precio debe ser mayor que cero")
return {
"nombre": normalizar_nombre(nombre),
"precio": precio,
}
Este módulo nos permitirá practicar pruebas sobre cadenas, diccionarios, validaciones y excepciones.
Ahora crea carrito.py dentro de tienda:
class Carrito:
def __init__(self):
self.items = []
def agregar(self, producto, cantidad=1):
if cantidad <= 0:
raise ValueError("La cantidad debe ser mayor que cero")
self.items.append({
"producto": producto,
"cantidad": cantidad,
})
def cantidad_total(self):
return sum(item["cantidad"] for item in self.items)
def subtotal(self):
total = 0
for item in self.items:
total += item["producto"]["precio"] * item["cantidad"]
return total
def vaciar(self):
self.items.clear()
Esta clase tiene estado interno. Agrega productos, calcula cantidades, calcula subtotales y permite vaciar el carrito. Más adelante la usaremos para practicar pruebas de clases y cambios de estado.
Aunque el objetivo del curso es automatizar pruebas, un archivo de ejecución manual nos ayuda a confirmar que el proyecto está conectado correctamente. Crea main.py en la raíz del proyecto:
from tienda.carrito import Carrito
from tienda.precios import calcular_total
from tienda.productos import validar_producto
def main():
producto = validar_producto(" teclado mecanico ", 50000)
carrito = Carrito()
carrito.agregar(producto, cantidad=2)
subtotal = carrito.subtotal()
total = calcular_total(subtotal, descuento=10, impuesto=21)
print("Producto:", producto["nombre"])
print("Cantidad:", carrito.cantidad_total())
print("Subtotal:", subtotal)
print("Total:", total)
if __name__ == "__main__":
main()
Desde la raíz del proyecto, ejecuta:
python main.py
La salida debería ser similar a:
Producto: Teclado Mecanico
Cantidad: 2
Subtotal: 100000
Total: 108900.0
Si aparece un error de importación, revisa que estés ubicado en la carpeta tienda-demo y que exista el archivo tienda/__init__.py.
Como este proyecto usará pytest, crea requirements.txt con este contenido:
pytest
En PowerShell puedes crearlo automáticamente con:
Set-Content -Path requirements.txt -Value "pytest"
Más adelante agregaremos herramientas como pytest-cov, pero por ahora alcanza con pytest.
Una prueba de humo verifica algo mínimo para confirmar que el proyecto puede importarse y ejecutarse sin errores básicos. Dentro de tests, crea test_humo.py:
from tienda.productos import validar_producto
def test_se_puede_crear_un_producto_valido():
producto = validar_producto("mouse", 12000)
assert producto == {
"nombre": "Mouse",
"precio": 12000,
}
No es una suite completa. Es solo una primera señal de que la estructura funciona y que pytest puede importar el paquete tienda.
Desde la raíz del proyecto, ejecuta:
python -m pytest
La salida esperada será similar a:
collected 1 item
tests/test_humo.py . [100%]
1 passed in 0.02s
Si la prueba pasa, ya tenemos un proyecto Python básico listo para empezar a escribir pruebas más específicas.
Al finalizar, la estructura debería quedar así:
tienda-demo/
|-- .venv/
|-- main.py
|-- requirements.txt
|-- tienda/
| |-- __init__.py
| |-- carrito.py
| |-- precios.py
| `-- productos.py
`-- tests/
`-- test_humo.py
Este proyecto fue elegido porque permite practicar varios tipos de pruebas sin agregar complejidad innecesaria:
aplicar_descuento y calcular_impuesto.En este proyecto, los archivos de la carpeta tienda representan el código que queremos probar. Los archivos dentro de tests representan las pruebas.
Separar ambas cosas ayuda a mantener el proyecto ordenado. También evita que las pruebas se mezclen con la lógica principal de la aplicación.
| Carpeta o archivo | Responsabilidad |
|---|---|
tienda/ |
Código principal del proyecto. |
tests/ |
Pruebas automatizadas. |
main.py |
Ejemplo de ejecución manual. |
requirements.txt |
Dependencias necesarias para instalar el entorno. |
__init__.py: puede causar problemas de importación según la estructura y la forma de ejecución.tienda.pytest no esté disponible.mkdir tienda-demo
cd tienda-demo
python -m venv .venv
.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
python -m pip install pytest
mkdir tienda
mkdir tests
New-Item tienda\__init__.py -ItemType File
python main.py
python -m pytest
En Linux o macOS, reemplaza la activación y la creación de __init__.py por:
source .venv/bin/activate
touch tienda/__init__.py
tests contendrá las pruebas automatizadas.pytest puede descubrir pruebas dentro de archivos llamados test_*.py.En este tema creamos un proyecto Python de ejemplo para usar durante el curso. El proyecto incluye módulos con cálculos, validaciones, una clase con estado y una primera prueba de humo.
A partir del próximo tema trabajaremos con más detalle sobre entornos virtuales, instalación de dependencias y comandos de ejecución, para que cada prueba pueda repetirse de manera clara y confiable.