Formato de Nombres para Pruebas Unitarias en Python
Resumen
Nombrar pruebas correctamente es clave para el éxito en equipo, facilitando la comprensión del código y la colaboración efectiva. Aunque no es obligatorio, tener un formato claro para las pruebas es muy beneficioso.
¿Cómo definir el formato de los nombres de las pruebas?
Todos los tests deben agruparse en clases, cada una relacionada con una clase de tu proyecto. Por ejemplo, si tienes una clase llamada BankAccount, la clase de prueba debería llamarse BankAccountTest.
Cada prueba debe comenzar con test_, para que las herramientas de testing la identifiquen fácilmente.
El siguiente elemento en el nombre debe ser el método que estás probando. Si es un método deposit, el nombre sería test_deposit_.
¿Cómo estructurar el escenario de la prueba?
Después del método, añade el escenario. Esto se refiere a los valores o parámetros que usas en la prueba. Por ejemplo, en el caso de un valor positivo en un depósito, el escenario sería positive_amount.
¿Cómo describir el resultado esperado?
Para finalizar el nombre, indica el resultado esperado. Si el depósito incrementa el saldo, añade algo como increase_balance. Así, un nombre de prueba completo sería: test_deposit_positive_amount_increase_balance.
¿Por qué es útil este formato?
Permite a cualquier miembro del equipo entender el propósito de la prueba sin revisar el código completo.
Facilita el mantenimiento del código y el soporte, ya que con solo leer el nombre, se entiende el objetivo de la prueba.
¿Qué reto se propone para mejorar las pruebas actuales?
Refactoriza las pruebas que has creado, probando diferentes escenarios y resultados posibles.
Imagina nuevas circunstancias para probar el código.
Compara tus resultados con el proyecto refactorizado que estará disponible en la sección de recursos, y comparte tus ideas.
Organizar y nombrar pruebas de manera efectiva en Python es fundamental para asegurar que las pruebas sean fáciles de mantener, claras, y cubran adecuadamente el código que estás probando. Aquí te dejo algunas **mejores prácticas** recomendadas para organizar y nombrar pruebas en Python:
### 1. **Organización de las Pruebas**
#### a. **Usar un Directorio Separado para las Pruebas**
Es una buena práctica colocar todas las pruebas en un directorio llamado tests/ en la raíz del proyecto. Esto ayuda a mantener las pruebas separadas del código fuente.
La estructura del directorio tests/ debe reflejar la estructura del código fuente. Por ejemplo, si tienes un archivo module1.py en tu código fuente, las pruebas correspondientes deberían estar en tests/test\_module1.py.
#### c. **Agrupar Pruebas en Clases**
Utiliza clases para agrupar pruebas relacionadas. Cada clase debería agrupar pruebas de un módulo o una funcionalidad específica. De esta forma, puedes aprovechar métodos como setUp y tearDown para inicializar o limpiar datos entre las pruebas.
import unittest
classCalculatorTests(unittest.TestCase):  def setUp(self):  \# Código para preparar el entorno de pruebas  pass     def tearDown(self):  \# Código para limpiar después de las pruebas  pass  def test\_add(self):  \# Prueba para la función de suma  pass     def test\_subtract(self):  \# Prueba para la función de resta  pass
#### d. **Un Archivo de Prueba por Módulo**
Cada módulo o clase del código fuente debería tener su archivo de pruebas correspondiente. Por ejemplo, si tienes un archivo module1.py, crea test\_module1.py con las pruebas para ese archivo.
### 2. **Nombrar las Pruebas**
#### a. **Nombrar Archivos de Pruebas**
Los archivos de pruebas deben seguir la convención de nombrarlos con el prefijo test\_ para que puedan ser detectados automáticamente por los frameworks de pruebas como unittest o pytest.
test\_module1.py
test\_module2.py
#### b. **Nombrar Métodos de Prueba**
Los métodos de prueba deben comenzar con test\_, seguido de una descripción clara de lo que están probando. Esto asegura que las pruebas sean descubiertas automáticamente y hace que su propósito sea evidente. Utiliza nombres descriptivos para los métodos, que reflejen claramente el comportamiento que se está probando.
classCalculatorTests(unittest.TestCase):  def test\_addition\_with\_positive\_numbers(self):  \# Prueba suma con números positivos  pass     def test\_division\_by\_zero\_raises\_error(self):  \# Prueba que la división por cero arroje un error  pass
#### c. **Descripciones Claras**
Asegúrate de que los nombres de las pruebas describan el comportamiento esperado, como qué función estás probando y en qué condiciones. Evita nombres genéricos como test\_1, test\_case o test\_function.
### 3. **Separar las Pruebas por Tipos**
#### a. **Pruebas Unitarias**
Las pruebas unitarias deben centrarse en probar unidades individuales de funcionalidad (funciones o métodos). Deben ser rápidas y no depender de recursos externos (como bases de datos o APIs).
#### b. **Pruebas de Integración**
En archivos separados o bajo una estructura diferente, incluye pruebas de integración que prueben cómo las diferentes partes del sistema interactúan entre sí.
### 4. **Utilizar Métodos de Configuración y Limpieza**
Cuando varias pruebas comparten datos o configuraciones, utiliza métodos como setUp, tearDown, setUpClass y tearDownClass de unittest o los equivalentes de pytest para reducir la repetición de código.
classDatabaseTests(unittest.TestCase):  @classmethod  def setUpClass(cls):  \# Código para configurar recursos una sola vez antes de todas las pruebas  pass     @classmethod  def tearDownClass(cls):  \# Código para liberar los recursos una vez después de todas las pruebas  pass  def setUp(self):  \# Código que se ejecuta antes de cada prueba individual  pass  def tearDown(self):  \# Código que se ejecuta después de cada prueba individual  pass
### 5. **Usar Nombres Descriptivos para las Clases de Prueba**
Al igual que los métodos de prueba, las clases de prueba también deben tener nombres descriptivos. Los nombres deben indicar qué clase o funcionalidad están probando.
classUserAuthenticationTests(unittest.TestCase):  def test\_login\_with\_valid\_credentials(self):  \# Prueba de inicio de sesión con credenciales válidas  pass
### 6. **Documentación y Comentarios**
Incluir comentarios breves o docstrings en las pruebas puede ayudar a aclarar lo que se está probando y por qué. Esto es especialmente útil si la prueba tiene una lógica compleja o si estás probando un caso límite específico.
classMathOperationsTests(unittest.TestCase):  def test\_square\_root\_of\_negative\_number\_raises\_error(self):  """Debe generar un ValueError cuando se intenta calcular la raíz cuadrada de un número negativo"""  with self.assertRaises(ValueError):  math.sqrt(-1)
### 7. **Evitar Dependencias entre Pruebas**
Cada prueba debe ser independiente y no debe depender de los resultados o el estado de otras pruebas. Esto garantiza que puedan ejecutarse de forma aislada, en cualquier orden.
### 8. **Ejecutar Pruebas Automáticamente**
Utiliza herramientas como pytest o unittest para ejecutar las pruebas de forma automática y generar informes. Puedes integrarlas en un sistema de integración continua (CI) como Travis CI o GitHub Actions.
### 9. **Uso de Frameworks de Prueba**
- **unittest**: Es el framework de pruebas estándar en Python y tiene una buena integración con la línea de comandos.
- **pytest**: Proporciona una sintaxis más simple y muchas características adicionales, como fixtures y el descubrimiento automático de pruebas.
### Conclusión
Una buena organización y una convención de nombres adecuada hacen que tus pruebas sean más mantenibles y fáciles de entender. Siguiendo estas prácticas, te asegurarás de que tus pruebas sean eficaces y fáciles de trabajar a largo plazo.
Bueno, en mi caso agregué pruebas para todos los posibles escenarios que se me ocurrieron en los 3 principales métodos (deposit, withdraw y transfer) y luego cree un directorio para almacenar las suites de pruebas por cada método para intentar mejorar la organización.
Buen trabajo!
Una pregunta, si el proyecto no usa clases, se debe tener igual la clase en el archivo de pruebas?
Puedes manejar solo funciones pero para proyectos que vayan a escarlar si es recomendable usar las clases.
en mi caso lo organicé de una manera similar a como se menciona, jajaja estos son los momentos en que agradezco el TOC.
Lo del path, me quedo la duda... alguien sabe bien como es eso? a donde se puede definir el python path, y que no se haga de forma manual siempre, como lo hizo el profesor?
O existe otra forma de importar paquetes sin definir el path?