Lección 20 · 20 de 20
Proyecto: un inventario con pruebas · Python
Un pequeño inventario en Python que reúne clases, dataclasses y excepciones propias, comprobado con pruebas assert. Ejecútelo en DMarlon Studio.

Este proyecto junta lo visto en el curso. Product es una clase de datos: tres campos, uno con valor por defecto, y un __repr__ gratis que se ve en la salida. OutOfStock es una excepción propia para el único error que el inventario sabe tratar.
Inventory es una clase normal. Su __init__ recibe cualquier número de productos con *products y los guarda en un diccionario por código (sku) con una comprensión. sell() busca el producto (si el código no existe salta un KeyError, un error distinto a propósito), comprueba las existencias, las descuenta y devuelve el importe. value() suma el valor de todo el almacén.
La segunda mitad son pruebas. `assert condición` no hace nada si la condición es cierta y lanza AssertionError si es falsa, con el mensaje opcional que va tras la coma. Cada assert fija un comportamiento: el valor inicial es 955, vender 4 sillas cobra 198 y quedan 6.
Para probar que algo falla, se intenta dentro de un try: si sell("TABLE", 5) no lanzara OutOfStock, se llegaría a assert False y la prueba fallaría. Como sí la lanza, el except la captura y muestra el mensaje. Si el programa llega a All tests passed, todas las comprobaciones se han cumplido.
En proyectos reales estas pruebas van en funciones test_... y las ejecuta pytest, que usa estos mismos assert. Ojo: python -O desactiva los assert, así que no los use para validar datos en producción.
Código · main.py
from dataclasses import dataclassclass OutOfStock(Exception): pass # our own error for this program@dataclassclass Product: sku: str price: float stock: int = 0class Inventory: def __init__(self, *products): self.products = {p.sku: p for p in products} def sell(self, sku, qty): product = self.products[sku] # KeyError if the SKU is unknown if qty > product.stock: raise OutOfStock(f"only {product.stock} x {sku} left") product.stock -= qty return qty * product.price def value(self): return sum(p.price * p.stock for p in self.products.values())shop = Inventory(Product("CHAIR", 49.5, 10), Product("TABLE", 230.0, 2))assert shop.value() == 955.0 # a test: stops here if Falseassert shop.sell("CHAIR", 4) == 198.0assert shop.products["CHAIR"].stock == 6, "stock was not updated"try: shop.sell("TABLE", 5) assert False, "OutOfStock expected"except OutOfStock as error: print("Expected error:", error)print(shop.products["CHAIR"], "| value:", shop.value())print("All tests passed")Salida · python3 main.py
Expected error: only 2 x TABLE left Product(sku='CHAIR', price=49.5, stock=6) | value: 757.0 All tests passed
Para recordar
- Una clase de datos para los datos y una clase normal para el comportamiento.
- Excepciones propias para los errores que el programa sabe tratar.
assert condición, "mensaje"detiene el programa si algo no cuadra.- Para probar un error:
try, la llamada,assert Falseyexcept. - pytest ejecuta funciones
test_...con estos mismosassert.
Ejercicio
Añada un método restock(sku, qty) que rechace cantidades negativas con ValueError, y escriba dos pruebas: una que reponga 3 mesas y otra que compruebe el error.