El método setUp en unittest te permite eliminar código repetido entre pruebas unitarias al ejecutar instrucciones comunes antes de cada test. Si estás aprendiendo testing en Python con clases como TestCase, dominar setUp es clave para escribir suites limpias, mantenibles y profesionales.
Para verlo en acción, dejamos atrás la calculadora del proyecto anterior y trabajamos con una clase BankAccount que permite depositar, retirar y consultar el saldo disponible.
¿Qué hace el método setUp en unittest?
Es un método heredado de TestCase que se ejecuta automáticamente antes de cada prueba dentro de la clase. Por defecto no hace nada, pero existe para que tú lo sobrescribas con la lógica de inicialización que necesites compartir.
¿Qué es setUp en unittest? Es un método especial de la clase TestCase que Python ejecuta antes de cada test del archivo. Sirve para preparar objetos, datos o estados que varias pruebas necesitan, evitando duplicar código.
En la práctica, todo lo que coloques dentro de setUp y guardes en self estará disponible en cada método de prueba como un atributo del objeto.
¿Cómo se prueba una clase BankAccount paso a paso?
Antes de hablar de setUp, conviene escribir las pruebas tal como saldrían de forma natural y luego refactorizar. Esa es la mejor manera de entender por qué setUp existe [00:32].
¿Cómo se estructura el archivo de pruebas?
El primer paso es crear un archivo test_bank_account.py, importar unittest e importar la clase BankAccount. La convención es nombrar la clase de prueba con el mismo nombre del objeto bajo prueba seguido de la palabra Test, y heredar de TestCase para reutilizar la infraestructura que Python ya trae.
¿Cómo escribir pruebas para depósito, retiro y balance?
Con la clase lista, defines tres métodos de prueba. Cada uno instancia una cuenta con un balance inicial de 1000 y valida un comportamiento distinto:
test_deposit: deposita 500 y verifica que el nuevo balance sea 1500.
test_withdraw: retira 200 y valida que el balance final sea 800.
test_get_balance: comprueba que get_balance devuelva 1000 al consultar la cuenta recién creada.
Para correrlas con más detalle, ejecutas el comando con la bandera -v, que activa el modo verbose y muestra el nombre de cada prueba, su ubicación y si pasó OK [02:14].
python
import unittest
from bank_account import BankAccount
Al revisar las tres pruebas, salta a la vista que cada una repite la misma línea: la creación de la cuenta con balance inicial. Ese patrón es la señal clara de que setUp debe entrar en escena [03:54].
¿Cuándo debo usar setUp? Úsalo cuando dos o más pruebas de la misma clase necesitan el mismo objeto inicial, datos de prueba o configuración. Si solo una prueba lo requiere, déjalo dentro de esa prueba.
¿Cómo eliminar código duplicado entre pruebas?
La solución es mover esa instanciación a setUp y guardarla en self.account. Como setUp corre antes de cada test, cada prueba recibe una cuenta nueva, aislada y consistente. Importante: no necesitas llamar a super().setUp() porque la implementación base no hace nada.
python
class BankAccountTest(unittest.TestCase):
def setUp(self):
self.account = BankAccount(balance=1000)
Al ejecutar de nuevo en modo verbose, las tres pruebas siguen pasando, pero el archivo es más corto, más legible y más fácil de mantener.
¿Por qué usar self dentro de setUp?
Porque self es la variable del objeto de prueba. Todo lo que asignes ahí queda disponible como atributo en cualquier método de la clase. Sin self, la cuenta solo viviría dentro del setUp y no podrías usarla en test_deposit, test_withdraw ni test_get_balance.
¿Qué reto puedes resolver para practicar setUp?
Para afianzar lo aprendido, agrega un método transfer a BankAccount que mueva dinero entre dos cuentas. Asegúrate de que levante una excepción cuando el saldo sea insuficiente.
Luego escribe dos pruebas unitarias:
Una que valide una transferencia exitosa entre dos cuentas con saldo suficiente.
Otra que verifique que se levanta la excepción cuando el saldo no alcanza.
Usa setUp para crear las dos cuentas que ambas pruebas necesitan. ¿Cómo resolverías el caso de la excepción? Comparte tu solución en los comentarios.
classBankAccount: # Inicia con saldo en cero
def __init__(self, balance=0): self.balance= balance
def deposit(self, amount): self.balance+= amount
return self.balance def withdraw(self, amount):if amount >0: self.balance-= amount
return self.balance def get_balance(self):return self.balance def transfer(self, amount, target):if self.balance< amount: raise ValueError("No hay fondos suficientes") self.withdraw(amount) target.deposit(amount)# Testsimportunittestfrom src.bank_accountimportBankAccountclassBankAccountTests(unittest.TestCase): # Definir setup ,para no repetir código
def setUp(self): self.account=BankAccount(balance=1000) def test_deposit(self): new_balance = self.account.deposit(500) # Nuevo balance es igual a 1500 self.assertEqual(new_balance,1500) def test_withdraw(self): new_balance = self.account.withdraw(200) self.assertEqual(new_balance,800) def test_get_balance(self): self.account.deposit(3000) self.account.withdraw(1000) actual_balance = self.account.get_balance() self.assertEqual(actual_balance,3000) def test_success_transfer(self): target =BankAccount(balance=500) self.account.transfer(200, target) # Balance cuenta emisora
self.assertEqual(self.account.get_balance(),800) # Balance cuenta receptora
self.assertEqual(target.get_balance(),700) def test_insufficient_funds_transfer(self): target =BankAccount(balance=500)with self.assertRaises(ValueError)ascontext: self.account.transfer(2000, target) self.assertEqual(str(context.exception),"No hay fondos suficientes")```classBankAccount: # Inicia con saldo en cero def \_\_init\_\_(*self*,*balance*=0):*self*.balance=*balance*  def deposit(*self*,*amount*):*self*.balance+=*amount*return*self*.balance  def withdraw(*self*,*amount*):if*amount*>0:*self*.balance-=*amount*return*self*.balance  def get\_balance(*self*):return*self*.balance  def transfer(*self*,*amount*,*target*):if*self*.balance<*amount*: raise ValueError("No hay fondos suficientes")*self*.withdraw(*amount*)*target*.deposit(*amount*)
*********** En los recursos de la clase existe un error ************************
Al ejecutar las pruebas, es útil utilizar el comando con la opción -b (acá debería ser -v )**** para obtener una salida más detallada y visualizar exactamente qué pruebas se están ejecutando y dónde están ubicadas en el código. Esto ayuda a depurar y tener un mejor control sobre el flujo de las pruebas.
Para ir a la documentación o archivo donde se implementa la clase TestCase como lo hace el profesor, pueden hacerlo en VScode con Ctrl + Click en la clase TestCase o clase que deseen buscar su referencia
def transfer(self,amount: int, target):if amount >0 and self.balance> amount: target.deposit(amount) self.balance-= amount
return self.balanceelse: raise ValueError('You do not have enough money')# Para el tests
def test_transfer(self): target =BankAccount(500) self.account.transfer(900, target) #Balance cuenta transmisora
self.assertEqual(self.account.get_balance(),100) #Balance cuenta receptora
self.assertEqual(target.get_balance(),1400) def test_insufficient_funds_transfer(self): target =BankAccount(balance=500)with self.assertRaises(ValueError)ascontext: self.account.transfer(2000, target) self.assertEqual(str(context.exception),"You do not have enough money")
import unittest
from src.bank_account import BankAccount
classBankAccountTest(unittest.TestCase):defsetUp(self): self.account = BankAccount(1000)deftest_deposit(self): res = self.account.deposit(500)assert res ==1500deftest_withdraw(self): res = self.account.withdraw(500)assert res ==500deftest_get_balance(self): res = self.account.get_balance()assert res ==1000deftest_transfer(self): target = BankAccount() res = self.account.transfer(target,500)assert res ==(500,500)deftest_transfer_insufficient_funds(self): target = BankAccount()with self.assertRaises(ValueError): self.account.transfer(target,1500)
Respuesta:
test_deposit (test_bank_account.BankAccountTest.test_deposit)... ok
test_get_balance (test_bank_account.BankAccountTest.test_get_balance)... ok
test_transfer (test_bank_account.BankAccountTest.test_transfer)... ok
test_transfer_insufficient_funds (test_bank_account.BankAccountTest.test_transfer_insufficient_funds)... ok
test_withdraw (test_bank_account.BankAccountTest.test_withdraw)... ok
test_div (test_calculator.TestCalculator.test_div)... ok
test_sum (test_calculator.TestCalculator.test_sum)... ok
----------------------------------------------------------------------
Ran 7 tests in0.001s
OK
Reto de la Clase!!
classBankAccount: def __init__(self, initial_balance=0): self.balance= initial_balance
def deposit(self, amount):if amount >0: self.balance+= amount
else: raise ValueError("Deposit amount must be positive")return self.balance def withdraw(self, amount):if amount > self.balance: raise ValueError("Insufficient funds") elif amount <=0: raise ValueError("Withdrawal amount must be positive")else: self.balance-= amount
return self.balance def get_balance(self):return self.balance def transfer(self, amount, target_account):if amount <=0: raise ValueError("Transfer amount must be positive")if amount > self.balance: raise ValueError("Insufficient funds for transfer") self.balance-= amount
target_account.deposit(amount)return self.balance