Pruebas automatizadas con Pytest

Se preparó una traducción del artículo específicamente para los estudiantes del curso de Ingeniero de Python QA .




Estamos viviendo en una era en la que el software se está moviendo muy rápidamente al mercado. Debido a esto, el proceso de desarrollo se vuelve muy estresante. Las altas tasas de implementación de software y la entrega rápida parecen una buena parte del modelo de negocio, pero aquí surge la pregunta acerca de cómo entregar software de la calidad adecuada.

¿Por qué necesitamos pruebas automatizadas?


Las pruebas automatizadas tienen muchas ventajas, aquí hay tres principales:
Reutilización: no es necesario escribir nuevos scripts cada vez, incluso cuando se lanza una nueva versión del sistema operativo, a menos que haya una necesidad urgente de ello.
Confiabilidad: las personas tienden a cometer errores, y los autos los hacen menos propensos. Y funcionan más rápido al realizar pasos / pruebas repetidas que deben realizarse continuamente.
Trabajo las 24 horas, los 7 días de la semana: puede comenzar a realizar pruebas en cualquier momento del día, incluso de forma remota. Si comienza a realizar pruebas por la noche, se ejecutará incluso mientras duerme.

Desarrolló una herramienta de prueba pytest con todas las funciones en Python


Actualmente, hay muchos marcos y herramientas para realizar pruebas. Existen diferentes tipos de marcos, por ejemplo, basados ​​en datos, basados ​​en palabras clave, híbridos, BDD, etc. Puede elegir el que mejor se adapte a sus necesidades.

Debo decir que Python y pytest ocupan un gran nicho en este asunto. Python y sus herramientas relacionadas son ampliamente utilizadas, probablemente porque son más accesibles para personas con poca experiencia en programación en comparación con otros lenguajes.

El marco pytest facilita la escritura de pequeñas pruebas, pero también se escala para admitir pruebas funcionales sofisticadas de aplicaciones y bibliotecas.

Algunas características clave de pytest :

  • Detección automática de módulos de prueba y funciones;
  • CLI eficaz para mejorar el control sobre lo que desea ejecutar u omitir;
  • Gran ecosistema de complementos de terceros;
  • Accesorios: diferentes tipos, diferentes aplicaciones;
  • Trabaje con el marco de prueba de unidad tradicional.

Detección de prueba automática y configurable.


Por defecto, pytest espera encontrar pruebas en esos módulos de Python cuyos nombres comienzan con test_ o terminan con _test.py . Además, de forma predeterminada, espera que los nombres de las funciones de prueba comiencen con el prefijo test_ . Sin embargo, este protocolo de detección de prueba se puede cambiar agregando su propia configuración a uno de los pytest configuración de pytest .

 # content of pytest.ini # Example 1: have pytest look for "check" instead of "test" # can also be defined in tox.ini or setup.cfg file, although the section # name in setup.cfg files should be "tool:pytest" [pytest] python_files = check_*.py python_classes = Check python_functions = *_check 

Veamos una función de prueba muy simple:

 class CheckClass(object): def one_check(self): x = "this" assert 'h' in x def two_check(self): x = "hello" assert hasattr(x, 'check') 

¿Has notado algo? No hay assertEqual o assertDictEqual , solo una assertDictEqual accesible y comprensible. No es necesario importar estas funciones para simplemente comparar dos objetos. Afirmar es lo que Python ya tiene y no hay necesidad de reinventar la rueda.

Código de plantilla? ¡No te preocupes, los accesorios corren al rescate!


Mire las funciones de prueba que prueban las operaciones básicas en el programa Wallet:

 // test_wallet.py from wallet import Wallet def test_default_initial_amount(): wallet = Wallet() assert wallet.balance == 0 wallet.close() def test_setting_initial_amount(): wallet = Wallet(initial_amount=100) assert wallet.balance == 100 wallet.close() def test_wallet_add_cash(): wallet = Wallet(initial_amount=10) wallet.add_cash(amount=90) assert wallet.balance == 100 wallet.close() def test_wallet_spend_cash(): wallet = Wallet(initial_amount=20) wallet.spend_cash(amount=10) assert wallet.balance == 10 wallet.close() 

Ejem, interesante! ¿Te has dado cuenta? Hay mucho código repetitivo. Otra cosa que vale la pena señalar es que esta prueba hace algo más además de probar la parte funcional, por ejemplo, crear una billetera y cerrarla con wallet.close() .

Ahora echemos un vistazo a cómo puede deshacerse del código repetitivo utilizando los pytest pytest.

 import pytest from _pytest.fixtures import SubRequest from wallet import Wallet #==================== fixtures @pytest.fixture def wallet(request: SubRequest): param = getattr(request, 'param', None) if param: prepared_wallet = Wallet(initial_amount=param[0]) else: prepared_wallet = Wallet() yield prepared_wallet prepared_wallet.close() #==================== tests def test_default_initial_amount(wallet): assert wallet.balance == 0 @pytest.mark.parametrize('wallet', [(100,)], indirect=True) def test_setting_initial_amount(wallet): assert wallet.balance == 100 @pytest.mark.parametrize('wallet', [(10,)], indirect=True) def test_wallet_add_cash(wallet): wallet.add_cash(amount=90) assert wallet.balance == 100 @pytest.mark.parametrize('wallet', [(20,)], indirect=True) def test_wallet_spend_cash(wallet): wallet.spend_cash(amount=10) assert wallet.balance == 10 

Bien, ¿no es así? Las funciones de prueba ahora son compactas y hacen exactamente lo que deberían hacer. La billetera se configura, instala y cierra usando el wallet . Los accesorios no solo ayudan a escribir código reutilizable, sino que también agregan el concepto de intercambio de datos. Si observa detenidamente, la cantidad en la wallet es parte de los datos de prueba proporcionados externamente por la lógica de prueba, y no rígidamente fijados dentro de la función.

 @pytest.mark.parametrize('wallet', [(10,)], indirect=True) 

En un entorno más controlado, puede tener un archivo con datos de prueba, por ejemplo test-data.ini en su repositorio o shell que puede leerlo, mientras que su función de prueba puede llamar a varios shells para leer datos de prueba.

Sin embargo, se recomienda colocar todos los dispositivos en un archivo especial conftest.py . Este es un archivo especial en pytest que permite que la prueba detecte dispositivos globales.

¡Pero tengo casos de prueba que quiero ejecutar en diferentes conjuntos de datos!


No se preocupe, pytest tiene una característica genial para parametrizar su dispositivo. Veamos un ejemplo.

Supongamos que su producto tiene una CLI que se administra localmente. Además, su producto tiene muchos parámetros predeterminados que se establecen en el inicio, y desea verificar todos los valores de estos parámetros.

Puede pensar en escribir un caso de prueba separado para cada uno de estos parámetros, ¡pero con pytest todo es mucho más simple!

 @pytest.mark.parametrize(“setting_name, setting_value”, [('qdb_mem_usage', 'low'), ('report_crashes', 'yes'), ('stop_download_on_hang', 'no'), ('stop_download_on_disconnect', 'no'), ('reduce_connections_on_congestion', 'no'), ('global.max_web_users', '1024'), ('global.max_downloads', '5'), ('use_kernel_congestion_detection', 'no'), ('log_type', 'normal'), ('no_signature_check', 'no'), ('disable_xmlrpc', 'no'), ('disable_ntp', 'yes'), ('ssl_mode', 'tls_1_2'),])def test_settings_defaults(self, setting_name, setting_value): assert product_shell.run_command(setting_name) == \ self.”The current value for \'{0}\' is \'{1}\'.”.format(setting_name, setting_value), \ 'The {} default should be {}'.format(preference_name, preference_value) 

Genial, ¿no es así? Acaba de escribir 13 casos de prueba (cada uno establece un setting_value diferente), y en el futuro, si agrega un nuevo parámetro a su producto, todo lo que necesita hacer es agregar otra tupla.

¿Cómo se integra pytest con las pruebas de interfaz de usuario con las pruebas Selenium y API?


Bueno, su producto puede tener varias interfaces. CLI - como dijimos anteriormente. Similar a la GUI y API. Antes de implementar su producto de software, es importante probarlos todos. En el software empresarial, donde varios componentes están interconectados y dependen unos de otros, un cambio en una parte puede afectar a todas las demás.

Recuerde que pytest es solo un marco para pruebas fáciles, no un tipo específico de prueba. Es decir, puede crear pruebas para la GUI utilizando Selenium o, por ejemplo, pruebas para la API con la biblioteca de requests de Python y ejecutarlas con pytest .

Por ejemplo, en un nivel alto, esto puede ser una verificación de la estructura del repositorio.



Como puede ver en la imagen de arriba, ofrece una buena oportunidad para separar los componentes:

apiobjects : un buen lugar para crear contenedores para llamar a puntos finales API. Puede tener un BaseAPIObject y una clase derivada que cumpla con sus requisitos.

ayudantes : puede agregar sus métodos de ayuda aquí.

lib : archivos de biblioteca que pueden ser utilizados por varios componentes, por ejemplo, sus elementos conftest en un conftest , pageobjects , etc.

pageobjects : el PageObjects arquitectura PageObjects se puede usar para crear clases para varias páginas GUI. Usamos Webium , que es una biblioteca de implementaciones de plantillas de objetos de página para Python.

suites : puede escribir sus propios conjuntos de comprobaciones de pylint para el código, lo ayudarán a ganar más confianza en la calidad de su código.

pruebas : puede catalogar las pruebas según sus preferencias. Esto facilitará la administración y revisión de sus pruebas.

Lo traje solo como referencia, la estructura del repositorio y las dependencias se pueden organizar de acuerdo con sus necesidades personales.

Tengo muchos casos de prueba y quiero que se ejecuten en paralelo


Puede tener muchos casos de prueba en su conjunto, y sucede que necesita ejecutarlos en paralelo y reducir el tiempo total de ejecución de la prueba.

Pytest ofrece un impresionante complemento de ejecución de prueba paralela llamado pytest-xdist , que agrega varios modos de ejecución únicos al pytest base. Instala este complemento usando pip .

 pip install pytest-xdist 

Veamos cómo funciona con un ejemplo.

Tengo un repositorio de pruebas automatizadas de CloudApp para mis pruebas de GUI de Selenium. Además, está en constante crecimiento y actualización con nuevas pruebas y ahora tiene cientos de pruebas. Lo que quiero hacer es ejecutarlos en paralelo y reducir el tiempo total de ejecución de la prueba.

En la terminal, simplemente escriba pytest en la carpeta raíz del proyecto / carpeta de prueba. Esto le permitirá ejecutar todas las pruebas.

 pytest -s -v -n=2 



pytest-xdist ejecutará todas las pruebas en paralelo!

De esta manera, también puede ejecutar múltiples navegadores en paralelo.

Informes


Pytest viene con soporte incorporado para crear archivos de resultados de prueba que se pueden abrir con Jenkins, Bamboo u otros servidores de integración continua. Use lo siguiente:

 pytest test/file/path — junitxml=path 

Esto ayudará a generar un gran archivo XML que se puede abrir con muchos analizadores.

Conclusión


La popularidad de Pytest está creciendo cada año. Además, tiene un poderoso soporte comunitario, que le permite acceder a muchas extensiones, como pytest-django , que lo ayudará a escribir pruebas para aplicaciones web en Django. Recuerde que pytest admite casos de prueba unittest, por lo que si usa unittest , pytest debe considerarse con más detalle.

Fuentes



Eso es todo. ¡Nos vemos en el curso !

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


All Articles