Prueba de Python con pytest. Configuración, CAPÍTULO 6

Atrás Siguiente


En este capítulo, veremos los archivos de configuración que afectan a pytest, discutiremos cómo pytest cambia su comportamiento en función de ellos y realizaremos algunos cambios en las tareas de configuración del proyecto Tareas.



Los ejemplos en este libro están escritos usando Python 3.6 y pytest 3.2. pytest 3.2 es compatible con Python 2.6, 2.7 y Python 3.3+.


El código fuente para el proyecto Tareas, así como para todas las pruebas que se muestran en este libro, está disponible en el enlace en la página web del libro en pragprog.com . No necesita descargar el código fuente para comprender el código de prueba; El código de prueba se presenta de forma conveniente en los ejemplos. Pero para seguir las tareas del proyecto o adaptar ejemplos de prueba para probar su propio proyecto (¡sus manos están desatadas!), Debe ir a la página web del libro y descargar el trabajo. Allí, en la página web del libro, hay un enlace para mensajes de erratas y un foro de discusión .

Debajo del spoiler hay una lista de artículos en esta serie.



Configuracion


Hasta ahora en este libro, he hablado sobre varios archivos que no son de prueba que afectan a pytest principalmente de pasada, con la excepción de conftest.py, que cubrí con algún detalle en el Capítulo 5, Complementos, en la página 95. En este capítulo, veremos los archivos de configuración, que afectan a pytest, analice cómo pytest cambia su comportamiento en función de ellos y realice algunos cambios en los archivos de configuración del proyecto Tareas.


Comprender los archivos de configuración de pytest


Antes de decirle cómo puede cambiar el comportamiento predeterminado en pytest, repasemos todos los archivos que no son de prueba en pytest y, en particular, quién debe encargarse de ellos.


Debe saber lo siguiente:


  • pytest.ini : este es el archivo de configuración principal de Pytest que le permite cambiar el comportamiento predeterminado. Como puede hacer bastantes cambios de configuración, la mayor parte de este capítulo está dedicado a la configuración que puede realizar en pytest.ini .
  • conftest.py : este es un complemento local que permite conectar funciones y dispositivos de conftest.py al directorio en el que conftest.py archivo conftest.py y todos sus subdirectorios. El archivo conftest.py describe en el capítulo 5 "Complementos" en la página 95.
  • __init__.py : cuando se coloca en cada subdirectorio de prueba, este archivo le permite tener nombres de archivo de prueba idénticos en varios directorios de prueba. Veremos un ejemplo de lo que podría salir mal sin los archivos __init__.py en los directorios de prueba en el artículo "Cómo evitar colisiones de nombres de archivos" en la página 120.

Si usa tox, le interesará:


  • tox.ini : este archivo es similar a pytest.ini , pero para tox . Sin embargo, puede poner su configuración de pytest lugar de tener el archivo tox.ini y el archivo pytest.ini , lo que le permite guardar un archivo de configuración. Tox se trata en el capítulo 7, “Uso de pytest con otras herramientas”, en la página 125.

Si desea distribuir un paquete de Python (por ejemplo, Tareas), este archivo será de interés:


  • setup.cfg : también es un archivo INI que afecta el comportamiento de setup.py . Puede agregar algunas líneas a setup.py para ejecutar la python setup.py test y ejecutar todas sus pruebas de pytest. Si está distribuyendo el paquete, es posible que ya tenga el archivo setup.cfg y puede usar este archivo para almacenar la configuración de Pytest. Verá cómo se hace esto en el Apéndice 4, “Empaquetado y distribución de proyectos de Python”, en la página 175.

No importa en qué archivo coloque la configuración de pytest, el formato será básicamente el mismo.


Para pytest.ini :


ch6 / format / pytest.ini

 [pytest] addopts = -rsxX -l --tb=short --strict xfail_strict = true ... more options ... 

Para tox.ini :


ch6 / format / tox.ini

 ... tox specific stuff ... [pytest] addopts = -rsxX -l --tb=short --strict xfail_strict = true ... more options ... 

Para setup.cfg :


ch6 / format / setup.cfg

 ... packaging specific stuff ... [tool:pytest] addopts = -rsxX -l --tb=short --strict xfail_strict = true ... more options ... 

La única diferencia es que el encabezado de sección para setup.cfg es [tool:pytest] lugar de [pytest] .


Enumere las opciones válidas de ini-file con pytest –help


Puede obtener una lista de todos los parámetros válidos para pytest.ini de pytest --help :


 $ pytest --help ... [pytest] ini-options in the first pytest.ini|tox.ini|setup.cfg file found: markers (linelist) markers for test functions empty_parameter_set_mark (string) default marker for empty parametersets norecursedirs (args) directory patterns to avoid for recursion testpaths (args) directories to search for tests when no files or directories are given in the command line. console_output_style (string) console output: classic or with additional progress information (classic|progress). usefixtures (args) list of default fixtures to be used with this project python_files (args) glob-style file patterns for Python test module discovery python_classes (args) prefixes or glob names for Python test class discovery python_functions (args) prefixes or glob names for Python test function and method discovery xfail_strict (bool) default for the strict parameter of xfail markers when not given explicitly (default: False) junit_suite_name (string) Test suite name for JUnit report junit_logging (string) Write captured log messages to JUnit report: one of no|system-out|system-err doctest_optionflags (args) option flags for doctests doctest_encoding (string) encoding used for doctest files cache_dir (string) cache directory path. filterwarnings (linelist) Each line specifies a pattern for warnings.filterwarnings. Processed after -W and --pythonwarnings. log_print (bool) default value for --no-print-logs log_level (string) default value for --log-level log_format (string) default value for --log-format log_date_format (string) default value for --log-date-format log_cli (bool) enable log display during test run (also known as "live logging"). log_cli_level (string) default value for --log-cli-level log_cli_format (string) default value for --log-cli-format log_cli_date_format (string) default value for --log-cli-date-format log_file (string) default value for --log-file log_file_level (string) default value for --log-file-level log_file_format (string) default value for --log-file-format log_file_date_format (string) default value for --log-file-date-format addopts (args) extra command line options minversion (string) minimally required pytest version xvfb_width (string) Width of the Xvfb display xvfb_height (string) Height of the Xvfb display xvfb_colordepth (string) Color depth of the Xvfb display xvfb_args (args) Additional arguments for Xvfb xvfb_xauth (bool) Generate an Xauthority token for Xvfb. Needs xauth. ... 

Verá todas estas configuraciones en este capítulo, con la excepción de doctest_optionflags , que se trata en el Capítulo 7, “Uso de pytest con otras herramientas”, en la página 125.


Los complementos pueden agregar opciones de archivo ini


La lista anterior de configuraciones no es una constante. Para complementos (y archivos conftest.py) es posible agregar opciones de archivo ini. Las opciones agregadas también se agregarán a la salida del comando pytest --help.
Ahora echemos un vistazo a algunos cambios de configuración que podemos hacer usando la configuración integrada del archivo .ini disponible en core pytest.


Cambiar las opciones de línea de comando predeterminadas


Ya ha utilizado algunas opciones de línea de comando para pytest , como -v/--verbose para salida detallada -l/--showlocals para ver variables locales con un seguimiento de pila para pruebas fallidas. Es posible que siempre use algunas de estas options—or prefiera usarlas them—for a project . Si instala addopts en pytest.ini para los parámetros que necesita, ya no tendrá que ingresarlos. Aquí hay un conjunto que me gusta:


 [pytest] addopts = -rsxX -l --tb=short --strict 

El -rsxX permite a pytest informar los motivos de todas las pruebas skipped , xfailed o xpassed . El modificador -l permite que pytest muestre un seguimiento de la pila para las variables locales en caso de cada falla. --tb=short eliminará la mayor parte del seguimiento de la pila. Sin embargo, dejará el archivo y el número de línea. La --strict prohíbe el uso de tokens si no están registrados en el archivo de configuración. Verá cómo hacer esto en la siguiente sección.


Registro de marcadores para evitar errores tipográficos


Los marcadores personalizados, como se describe en “Funciones de prueba de etiquetado” en la página 31, son excelentes para permitirle marcar un subconjunto de pruebas para ejecutar con un marcador específico. Sin embargo, es demasiado fácil cometer un error en el marcador y, en última instancia, algunas pruebas se etiquetan @pytest.mark.smoke y otras se etiquetan @pytest.mark.somke . Por defecto, esto no es un error. pytest solo cree que creaste dos marcadores. Sin embargo, esto se puede solucionar registrando tokens en pytest.ini, por ejemplo, así:


 [pytest] ... markers = smoke: Run the smoke test test functions get: Run the test functions that test tasks.get() ... 

Al registrar estos marcadores, ahora también puede verlos con pytest --markers con sus descripciones:


 $ cd /path/to/code/ch6/b/tasks_proj/tests $ pytest --markers @pytest.mark.smoke: Run the smoke test test functions @pytest.mark.get: Run the test functions that test tasks.get() @pytest.mark.skip(reason=None): skip the ... ... 

Si los marcadores no están registrados, no aparecerán en la lista de --markers . Cuando se registran, se muestran en la lista, y si usa --strict , cualquier token con errores o no registrado se muestra como error. La única diferencia entre ch6/a/tasks_proj y ch6/b/tasks_proj es el contenido del archivo pytest.ini. En ch6/a vacío. Intentemos ejecutar las pruebas sin registrar ningún marcador:


 $ cd /path/to/code/ch6/a/tasks_proj/tests $ pytest --strict --tb=line ============================= test session starts ============================= collected 45 items / 2 errors =================================== ERRORS ==================================== ______________________ ERROR collecting func/test_add.py ______________________ 'smoke' not a registered marker ________________ ERROR collecting func/test_api_exceptions.py _________________ 'smoke' not a registered marker !!!!!!!!!!!!!!!!!!! Interrupted: 2 errors during collection !!!!!!!!!!!!!!!!!!! =========================== 2 error in 1.10 seconds =========================== 

Si usa marcadores en pytest.ini para registrar sus marcadores, también puede agregar --strict a sus addopts mientras lo hace. Me lo agradecerás más tarde. Avancemos y agreguemos un archivo pytest.ini al proyecto de tareas:


Si usa marcadores en pytest.ini para registrar sus marcadores, también puede agregar --strict a sus addopts . Me lo agradecerás más tarde. Continuemos y agreguemos el archivo pytest.ini al proyecto de tarea:


Si usa tokens en pytest.ini para registrar tokens, también puede agregar --strict a los existentes con addopts . Genial? pytest.ini gracias y agregue el archivo pytest.ini al proyecto de tasks :


ch6 / b / task_proj / tests / pytest.ini

 [pytest] addopts = -rsxX -l --tb=short --strict markers = smoke: Run the smoke test test functions get: Run the test functions that test tasks.get() 

Aquí está la combinación de banderas preferidas por defecto:


  • -rsxX para -rsxX qué pruebas se omiten, xfailed o xpassed,
  • --tb = short para una traza más corta en fallas,
  • --strict para permitir solo tokens declarados.
    Y una lista de marcadores para el proyecto.

Esto debería permitirnos realizar pruebas, incluidas pruebas de humo:


 $ cd /path/to/code/ch6/b/tasks_proj/tests $ pytest --strict -m smoke ===================== test session starts ====================== collected 57 items func/test_add.py . func/test_api_exceptions.py .. ===================== 54 tests deselected ====================== =========== 3 passed, 54 deselected in 0.06 seconds ============ 

Requisito mínimo de Pytest


El parámetro minversion permite especificar la versión mínima de pytest esperada para las pruebas. Por ejemplo, tenía la intención de usar approx() al probar números de punto flotante para determinar la igualdad "bastante cercana" en las pruebas. Pero esta función no se introdujo en pytest hasta la versión 3.0. Para evitar confusiones, agrego lo siguiente a los proyectos que usan approx() :


 [pytest] minversion = 3.0 

Por lo tanto, si alguien intenta ejecutar pruebas con una versión anterior de pytest, aparecerá un mensaje de error.


Evite que Pytest busque en los lugares equivocados


¿Sabía que una de las definiciones de "recurse" es jurar dos veces en su propio código? Pues no. De hecho, esto significa contabilizar subdirectorios. pytest permitirá la detección de pruebas al examinar de forma recursiva un montón de directorios. Pero hay algunos directorios que desea excluir de la visualización de Pytest.


El valor predeterminado para norecurse es '. * Build dist CVS _darcs {arch} and *.egg. Having '.*' '. * Build dist CVS _darcs {arch} and *.egg. Having '.*' '. * Build dist CVS _darcs {arch} and *.egg. Having '.*' Es una buena razón para nombrar su entorno virtual '.venv', porque todos los directorios que comienzan con un punto no serán visibles.


En el caso del proyecto Tareas, src no hará daño al especificar, porque buscar en los archivos de prueba usando pytest será una pérdida de tiempo.


 [pytest] norecursedirs = .* venv src *.egg dist build 

Al anular un parámetro que ya tiene un valor útil, como este parámetro, es útil saber cuáles son los valores predeterminados y devolver los que necesita, como hice en el código anterior con *.egg dist build .
norecursedirs es un tipo de consecuencia para las rutas de prueba, así que echemos un vistazo a esto más adelante.


especificación de árbol de directorio de prueba


Mientras que norecursedirs le dice a pytest dónde mirar, testpaths le dice a pytest dónde mirar. testspaths es una lista de directorios relativos al directorio raíz para encontrar pruebas. Se usa solo si el directorio, archivo o nodeid no se especifica como argumento.


Supongamos que para un proyecto de Tasks colocamos pytest.ini en el directorio tasks_proj lugar de pruebas:


 \code\tasks_proj>tree/f . │ pytest.ini │ ├───src │ └───tasks │ api.py │ ... │ └───tests │ conftest.py │ pytest.ini │ ├───func │ test_add.py │ ... │ ├───unit │ test_task.py │ __init__.py │ ... 

Entonces podría tener sentido poner las pruebas en testpaths de testpaths :


 [pytest] testpaths = tests 

Ahora, si ejecuta pytest desde el directorio task_proj, pytest solo buscará en tasks_proj/tests . El problema aquí es que durante el desarrollo y la depuración de las pruebas, a menudo itero sobre el directorio de prueba, por lo que puedo probar fácilmente un subdirectorio o archivo sin especificar la ruta completa. Por lo tanto, esta opción me ayuda un poco en las pruebas interactivas.


Sin embargo, es ideal para pruebas que se ejecutan desde un servidor de integración continua o tox. En estos casos, sabe que el directorio raíz será fijo y puede enumerar los directorios relativos a ese directorio raíz fijo. Estos también son los casos en los que realmente desea reducir el tiempo de prueba, por lo que deshacerse de la búsqueda de pruebas es excelente.


A primera vista, puede parecer una tontería usar tanto rutas de prueba como no norecursedirs al mismo tiempo. Sin embargo, como ya ha visto, las rutas de prueba ayudan poco en las pruebas interactivas desde diferentes partes del sistema de archivos. En estos casos, los no norecursedirs pueden ayudar. Además, si tiene directorios de prueba que no contienen pruebas, puede usar norecursedirs para evitarlos. Pero en realidad, ¿qué sentido tiene poner directorios adicionales en pruebas que no tienen pruebas?


Cambio de reglas de detección de prueba


pytest encuentra pruebas para ejecutar en base a reglas específicas de descubrimiento de pruebas. Reglas de detección de prueba estándar:


• Comience con uno o más directorios. Puede especificar los nombres de archivos o directorios en la línea de comando. Si no especificó nada, se utiliza el directorio actual.
• Busque en el catálogo y en todos sus subdirectorios los módulos de prueba.
• Un módulo de prueba es un archivo con un nombre similar a test_*.py o *_test.py .
• Busque en los módulos de prueba las funciones que comienzan con la prueba .
• Busque clases que comienzan con Test. Busque métodos en las clases que comienzan con `test , init` .


Estas son reglas de detección estándar; Sin embargo, puedes cambiarlos.


python_classes


La regla habitual para encontrar pruebas para pytest y clases es considerar una clase como una clase de prueba potencial si comienza con Test* . La clase tampoco puede tener el __init__() . Pero, ¿qué pasa si queremos nombrar nuestras clases de prueba como <something>Test o <something>Suite ? Aquí es donde entra python_classes :


 [pytest] python_classes = *Test Test* *Suite 

Esto nos permite nombrar las clases así:


 class DeleteSuite(): def test_delete_1(): ... def test_delete_2(): ... .... 

python_files


Al igual que pytest_classes , python_files modifica la regla de detección de prueba predeterminada, que consiste en encontrar archivos que comienzan con test_* o tienen *_test al final.
Supongamos que tiene un marco de prueba personalizado en el que nombra todos sus archivos de prueba check_<something>.py . Parece razonable En lugar de renombrar todos sus archivos, simplemente agregue una línea a pytest.ini siguiente manera:


 [pytest] python_files = test_* *_test check_* 

Muy simple Ahora puede transferir gradualmente la convención de nomenclatura si lo desea, o simplemente dejarla como check_* .


Python_functions


python_functions actúa como las dos configuraciones anteriores, pero para funciones de prueba y nombres de métodos. El valor predeterminado es test_* . Y para agregar check_* adivinado, haz esto:


 [pytest] python_functions = test_* check_* 

Las pytest nombres de pytest no parecen tan restrictivas, ¿verdad? Entonces, si no le gusta la convención de nomenclatura predeterminada, simplemente cámbiela. Sin embargo, le insto a que tenga una razón más convincente para tales decisiones. La migración de cientos de archivos de prueba es definitivamente una buena razón.


Prohibición XPASS


Establecer xfail_strict = true significa que las pruebas marcadas con @pytest.mark.xfail no se reconocen como causantes del error. Creo que esta configuración siempre debería ser. Para obtener más información sobre el token xfail consulte “Marcado de pruebas en espera de falla” en la página 37.


Prevenir conflictos de nombre de archivo


La utilidad de tener el archivo __init__.py en cada subdirectorio de prueba del proyecto me confundió durante mucho tiempo. Sin embargo, la diferencia entre tenerlos o no tenerlos es simple. Si tiene archivos __init__.py en todos sus subdirectorios de prueba, puede tener el mismo nombre de archivo de prueba en varios directorios. Y si no, entonces esto no funcionará.


Aquí hay un ejemplo. Los directorios b tienen el archivo test_foo.py . No importa lo que contengan estos archivos, pero para este ejemplo se ven así:


ch6 / dups / a / test_foo.py
 def test_a(): pass 


ch6 / dups / b / test_foo.py
 def test_b(): pass 

Con esta estructura de directorios:


 dups ├── a │ └── test_foo.py └── b └── test_foo.py 

Estos archivos ni siquiera tienen el mismo contenido, pero las pruebas están dañadas. Podrá ejecutarlos por separado, pero no hay forma de ejecutar pytest desde el directorio dups :


 $ cd /path/to/code/ch6/dups $ pytest a ============================= test session starts ============================= collected 1 item a\test_foo.py . ========================== 1 passed in 0.05 seconds =========================== $ pytest b ============================= test session starts ============================= collected 1 item b\test_foo.py . ========================== 1 passed in 0.05 seconds =========================== $ pytest ============================= test session starts ============================= collected 1 item / 1 errors =================================== ERRORS ==================================== _______________________ ERROR collecting b/test_foo.py ________________________ import file mismatch: imported module 'test_foo' has this __file__ attribute: /path/to/code/ch6/dups/a/test_foo.py which is not the same as the test file we want to collect: /path/to/code/ch6/dups/b/test_foo.py HINT: remove __pycache__ / .pyc files and/or use a unique basename for your test file modules !!!!!!!!!!!!!!!!!!! Interrupted: 1 errors during collection !!!!!!!!!!!!!!!!!!! =========================== 1 error in 0.34 seconds =========================== 

¡Nada está claro!
Este mensaje de error no indica qué salió mal.


Para corregir esta prueba, simplemente agregue el archivo __init__.py vacío a los subdirectorios. Aquí hay un ejemplo del directorio dups_fixed con los mismos nombres de archivos duplicados, pero con los archivos __init__.py agregados:


 dups_fixed/ ├── a │ ├── __init__.py │ └── test_foo.py └── b ├── __init__.py └── test_foo.py 

Ahora intentemos nuevamente desde el nivel superior en dups_fixed :


 $ cd /path/to/code/ch6/ch6/dups_fixed/ $ pytest ============================= test session starts ============================= collected 2 items a\test_foo.py . b\test_foo.py . ========================== 2 passed in 0.15 seconds =========================== 

Entonces será mejor.


Por supuesto, puede convencerse de que nunca tendrá nombres de archivos duplicados, por lo que eso no importa. Todo, como, es normal. Pero los proyectos están creciendo y los catálogos de prueba están creciendo, ¿y definitivamente quieres esperar a que te suceda antes de ocuparte? Yo digo, solo pon estos archivos allí. Hazlo un hábito y no te preocupes por eso otra vez.


Ejercicios


En el Capítulo 5, Complementos, en la página 95, creó un complemento llamado pytest-nice que incluía una opción de línea de comandos --nice. Extendamos eso para incluir una opción de pytest.ini llamada nice.


En el Capítulo 5, “Complementos” en la página 95, creó un complemento llamado pytest-nice que incluye la --nice línea de comandos --nice . pytest.ini esto para incluir la opción pytest.ini llamada nice .


  1. Agregue la siguiente línea a la pytest_addoption pytest_nice.py pytest_addoption pytest_nice.py : parser.addini('nice', type='bool', help='Turn failures into opportunities.')
  2. Los lugares en el complemento que usan getoption() también tendrán que llamar a getini('nice') . Haz estos cambios.
  3. Verifique esto manualmente agregando nice al archivo pytest.ini .
  4. No te olvides de las pruebas de complementos. Agregue una prueba para verificar que el nice parámetro de pytest.ini funciona correctamente.
  5. Agregue pruebas al directorio de complementos. Necesita encontrar algunas características adicionales de Pytester .

Que sigue


Si bien pytest es extremadamente poderoso en sí mismo, especialmente con complementos, también se integra bien con otras herramientas de desarrollo y prueba de software. En el próximo capítulo, examinaremos el uso de pytest junto con otras potentes herramientas de prueba.


Atrás Siguiente

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


All Articles