Introducción de pruebas en Python. Parte 2

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 Framework

Si 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 aplicaciones

Piense 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): # Your test methods 

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('/') # Make your assertions 

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 avanzados

Antes de comenzar a crear pruebas para su aplicación, recuerde los tres pasos principales de cualquier prueba:

  1. Creación de parámetros de entrada;
  2. Ejecución de código, recibiendo datos de salida;
  3. 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 esperadas

Anteriormente, 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ón

En 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ón

Hasta 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 datos

Muchas 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): # Load test data self.app = App(database='fixtures/test_basic.json') def test_customer_count(self): self.assertEqual(len(self.app.customers), 100) def test_existence_of_customer(self): customer = self.app.get_customer(id=10) self.assertEqual(customer.name, "Org XYZ") self.assertEqual(customer.address, "10 Red Road, Reading") class TestComplexData(unittest.TestCase): def setUp(self): # load test data self.app = App(database='fixtures/test_complex.json') def test_customer_count(self): self.assertEqual(len(self.app.customers), 10000) def test_existence_of_customer(self): customer = self.app.get_customer(id=9999) self.assertEqual(customer.name, u"バナナ") self.assertEqual(customer.address, "10 Red Road, Akihabara, Tokyo") if __name__ == '__main__': unittest.main() 

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 .

Source: https://habr.com/ru/post/es433572/


All Articles