Hola a todos!
Continuamos el
artículo sobre el conocimiento de las pruebas en Python, que hemos preparado para usted como parte de nuestro curso para
Desarrolladores de Python .
Prueba para Django y Flask Web FrameworkSi escribe pruebas para aplicaciones web utilizando uno de los marcos populares, por ejemplo, Django o Flask, entonces vale la pena recordar las diferencias importantes en la escritura y ejecución de tales pruebas.
Cómo se diferencian de otras aplicacionesPiense en el código que desea probar en una aplicación web. Todas las rutas, vistas y modelos requieren muchas importaciones y conocimiento sobre el marco utilizado.
Esto es similar a probar un automóvil, que se discutió en la primera parte del tutorial: antes de realizar pruebas simples, como verificar los faros, debe encender la computadora en el automóvil.
Django y Flask simplifican esta tarea y proporcionan un marco de prueba basado en pruebas unitarias. Puede continuar escribiendo pruebas de la manera habitual, pero ejecutarlas de manera un poco diferente.
Cómo usar el ejecutor de pruebas de Django
La plantilla startapp de Django crea el archivo tests.py en el directorio de su aplicación. Si aún no está allí, créelo con los siguientes contenidos:
from django.test import TestCase class MyTestCase(TestCase):
La principal diferencia con los ejemplos anteriores es que debe heredar de
django.test.TestCase
, no
unittest.TestCase
. La API de estas clases es la misma, pero la clase Django TestCase configura todo para las pruebas.
Para ejecutar un conjunto de pruebas, use la prueba
manage.py
lugar de unittest en la línea de comando:
$ python manage.py test
Si necesita varios archivos de prueba, reemplace tests.py con la carpeta de pruebas, coloque un archivo vacío llamado
__init__.py
y cree
test_*.py
archivos de
test_*.py
. Django los detectará y ejecutará.
Más información está disponible en
el sitio de documentación de Django .
Cómo usar Unittest y Flask
Para trabajar con Flask, una aplicación debe importarse y ponerse en modo de prueba. Puede crear un cliente de prueba y usarlo para enviar solicitudes a cualquier ruta en su aplicación.
El cliente de prueba se instancia en el método de configuración de su caso de prueba. En el siguiente ejemplo, my_app es el nombre de la aplicación. No se preocupe si no sabe lo que hace la configuración. Echaremos un vistazo más de cerca a la sección "Scripts de prueba más avanzados".
El código en el archivo de prueba se verá así:
import my_app import unittest class MyTestCase(unittest.TestCase): def setUp(self): my_app.app.testing = True self.app = my_app.app.test_client() def test_home(self): result = self.app.get('/')
Luego puede ejecutar casos de prueba utilizando el
python -m unittest discover.
Hay más información disponible en el sitio de documentación de Flask.
Scripts de prueba más avanzadosAntes de comenzar a crear pruebas para su aplicación, recuerde los tres pasos principales de cualquier prueba:
- Creación de parámetros de entrada;
- Ejecución de código, recibiendo datos de salida;
- Comparación de datos de salida con el resultado esperado;
Esto puede ser más complicado que crear un valor estático para los datos de origen, como una cadena o un número. A veces su aplicación requiere una instancia de una clase o contexto. ¿Qué hacer en este caso?
Los datos que crea como fuente se llaman fixture. Crear y reutilizar accesorios es una práctica común.
Ejecutar la misma prueba varias veces con diferentes valores en anticipación del mismo resultado se llama parametrización.
Manejo de fallas esperadasAnteriormente, cuando compilamos una lista de scripts para probar
sum()
, surgió la pregunta: ¿qué sucede cuando proporcionamos un valor incorrecto, por ejemplo, un solo entero o una cadena?
En este caso, se espera que
sum()
arroje un error. Si se produce un error, la prueba fallará.
Hay una forma específica de manejar los errores esperados. Puede usar
.assertRaises()
como administrador de contexto y luego realizar pasos de prueba dentro del bloque
with
:
import unittest from my_sum import sum class TestSum(unittest.TestCase): def test_list_int(self): """ , """ data = [1, 2, 3] result = sum(data) self.assertEqual(result, 6) def test_list_fraction(self): """ , """ data = [Fraction(1, 4), Fraction(1, 4), Fraction(2, 5)] result = sum(data) self.assertEqual(result, 1) def test_bad_type(self): data = "banana" with self.assertRaises(TypeError): result = sum(data) if __name__ == '__main__': unittest.main()
Este caso de prueba se pasará solo si
sum(data)
arroja un TypeError. Puede reemplazar TypeError con cualquier otro tipo de excepción.
Aislamiento del comportamiento de la aplicaciónEn la última parte del tutorial, hablamos sobre los efectos secundarios. Complican las pruebas unitarias, ya que cada prueba puede producir un resultado diferente o peor: ¡una prueba puede afectar el estado de toda la aplicación y hacer que otra prueba falle!
Existen algunas técnicas simples para probar partes de una aplicación con muchos efectos secundarios:
- Refactorización de código de acuerdo con el Principio de Responsabilidad Única;
- Burlándose de todos los métodos y llamadas a funciones para eliminar los efectos secundarios;
- Usar pruebas de integración en lugar de pruebas unitarias para este fragmento de la aplicación.
- Si no está familiarizado con la burla, consulte algunos excelentes ejemplos de Python CLI Testing .
Escritura de pruebas de integraciónHasta ahora, hemos prestado más atención a las pruebas unitarias. Las pruebas unitarias son una excelente manera de crear código predecible y estable. ¡Pero al final, su aplicación debería funcionar al inicio!
Las pruebas de integración son necesarias para verificar la colaboración de varios componentes de la aplicación. Dichas pruebas pueden requerir representar el rol del comprador o usuario:
- Llame a la API REST de HTTP;
- Llamada API Python;
- Llamada de servicio web;
- Ejecute la línea de comando.
Todos estos tipos de pruebas de integración se pueden escribir de la misma manera que las pruebas unitarias, siguiendo los parámetros de entrada de plantilla, ejecución, aprobación. La diferencia más significativa es que las pruebas de integración prueban simultáneamente más componentes, lo que significa que provocarán más efectos secundarios que las pruebas unitarias. Además, las pruebas de integración requieren más accesorios, como una base de datos, un socket de red o un archivo de configuración.
Por lo tanto, se recomienda separar las pruebas unitarias y las pruebas de integración. Crear dispositivos para los de integración, por ejemplo, una base de datos de prueba o casos de prueba en sí mismos, lleva mucho más tiempo que realizar pruebas unitarias, por lo que debe realizar pruebas de integración antes de entrar en producción en lugar de ejecutarlas cada vez que se compromete.
La forma más simple de separar las pruebas de unidad e integración es colocarlas en diferentes carpetas.
project/
│
├── my_app/
│ └── __init__.py
│
└── tests/
|
├── unit/
| ├── __init__.py
| └── test_sum.py
|
└── integration/
├── __init__.py
└── test_integration.py
Puede ejecutar un grupo específico de pruebas de diferentes maneras. Se puede agregar una marca para especificar el directorio de origen, -s, a unittest discover con una ruta que contiene pruebas:
$ python -m unittest discover -s tests/integration
unittest mostrará todos los resultados en el directorio de pruebas / integración.
Prueba de aplicaciones orientadas a datosMuchas pruebas de integración requieren datos de fondo, como una base de datos con valores específicos. Suponga que necesita una prueba para verificar el correcto funcionamiento de la aplicación con más de 100 clientes en la base de datos, o para verificar la exactitud de la visualización de la página de pedido, incluso si todos los nombres de los productos están en japonés.
Estos tipos de pruebas de integración dependerán de varios dispositivos de prueba para garantizar su repetibilidad y previsibilidad.
Los datos de prueba deben almacenarse en la carpeta de dispositivos dentro del directorio de pruebas de integración para enfatizar su "capacidad de prueba". Luego, en las pruebas, puede cargar los datos y ejecutar la prueba.
Aquí hay un ejemplo de una estructura de datos que consta de archivos JSON:
project/
│
├── my_app/
│ └── __init__.py
│
└── tests/
|
└── unit/
| ├── __init__.py
| └── test_sum.py
|
└── integration/
|
├── fixtures/
| ├── test_basic.json
| └── test_complex.json
|
├── __init__.py
└── test_integration.py
En el caso de prueba, puede usar el método .setUp () para cargar datos de prueba desde el archivo de dispositivo de una manera conocida y ejecutar varias pruebas con estos datos. Recuerde que puede almacenar varios casos de prueba en un archivo Python, unittest los encontrará y ejecutará. Puede tener un caso de prueba para cada conjunto de datos de prueba:
import unittest class TestBasic(unittest.TestCase): def setUp(self):
Si su aplicación depende de datos de una ubicación remota, como una API remota, asegúrese de que las pruebas sean repetibles. El desarrollo puede retrasarse debido a las pruebas que fallaron al deshabilitar la API y los problemas de comunicación. En tales casos, es mejor almacenar dispositivos remotos localmente para ser devueltos y enviados a la aplicación.
La biblioteca de
requests
tiene un paquete de respuesta gratuito que le permite crear dispositivos de respuesta y guardarlos en carpetas de prueba. Obtenga más información
en su página de GitHub .
La siguiente parte será sobre pruebas en varios entornos y pruebas de automatización.
El fin
Comentarios / preguntas son siempre bienvenidos. Aquí o ve a
Stas en un
día abierto .