Saltar al contenido

Python: uso de pytest para omitir la prueba a menos que se especifique

Solución:

Los documentos describen exactamente su problema: https://docs.pytest.org/en/latest/example/simple.html#control-skipping-of-tests-according-to-command-line-option. Copiando desde allí:

Aquí hay un archivo conftest.py que agrega una opción de línea de comando –runslow para controlar la omisión de las pruebas marcadas con pytest.mark.slow:

# content of conftest.py

import pytest


def pytest_addoption(parser):
    parser.addoption(
        "--runslow", action="store_true", default=False, help="run slow tests"
    )


def pytest_collection_modifyitems(config, items):
    if config.getoption("--runslow"):
        # --runslow given in cli: do not skip slow tests
        return
    skip_slow = pytest.mark.skip(reason="need --runslow option to run")
    for item in items:
        if "slow" in item.keywords:
            item.add_marker(skip_slow)

Ahora podemos escribir un módulo de prueba como este:

# content of test_module.py
import pytest


def test_func_fast():
    pass


@pytest.mark.slow
def test_func_slow():
    pass

Hay un par de formas de manejar esto, pero repasaré dos enfoques comunes que he visto en las líneas base de Python.

1) Separe sus pruebas colocando las pruebas “opcionales” en otro directorio.

No estoy seguro de cómo se ve el diseño de su proyecto, pero puede hacer algo como esto (solo el directorio de prueba es importante, el resto es solo un diseño de ejemplo de juguete):

README.md
setup.py
requirements.txt
test/
    unit/
        test_something.py
        test_something_else.py
    integration/
        test_optional.py
application/
    __init__.py
    some_module.py

Luego, cuando invoca pytest, lo invoca haciendo pytest test/unit si quieres correr solo las pruebas unitarias (es decir, solo test_something*.py archivos), o pytest test/integration si quieres correr solo las pruebas de integración (es decir, solo test_optional.py), o pytest test si quieres correr todos los exámenes. Entonces, de forma predeterminada, puede ejecutar pytest test/unit.

Recomiendo envolver estas llamadas en algún tipo de script. yo prefiero make ya que es potente para este tipo de envoltura. Entonces puedes decir make test y solo ejecuta su conjunto de pruebas predeterminado (rápido), o make test_ally ejecutará todas las pruebas (que pueden ser lentas o no).

Ejemplo de Makefile con el que podrías envolver:

.PHONY: all clean install test test_int test_all uninstall

all: install

clean:
    rm -rf build
    rm -rf dist
    rm -rf *.egg-info

install:
    python setup.py install

test: install
    pytest -v -s test/unit

test_int: install
    pytest -v -s test/integration

test_all: install
    pytest -v -s test

uninstall:
    pip uninstall app_name

2) Marque sus pruebas con prudencia con el @pytest.mark.skipif decorador, pero use una variable de entorno como desencadenante

No me gusta tanto esta solución, me parece un poco fortuita (es difícil saber qué conjunto de pruebas se están ejecutando en cualquier pytest correr). Sin embargo, lo que puede hacer es definir una variable de entorno y luego conectar esa variable de entorno al módulo para detectar si desea ejecutar todas sus pruebas. Las variables de entorno dependen del shell, pero pretenderé que tiene un entorno bash, ya que es un shell popular.

Podrías hacerlo export TEST_LEVEL="unit" solo para pruebas unitarias rápidas (por lo que este sería el predeterminado), o export TEST_LEVEL="all" para todas tus pruebas. Luego, en sus archivos de prueba, puede hacer lo que originalmente intentaba hacer así:

import os

...

@pytest.mark.skipif(os.environ["TEST_LEVEL"] == "unit")
def test_scrape_website():
  ...

Nota: Nombrar los niveles de prueba “unidad” e “integración” es irrelevante. Puedes nombrarlos como quieras. También puede tener muchos niveles (como pruebas nocturnas o pruebas de rendimiento).

Además, creo que la opción 1 es la mejor manera de hacerlo, ya que no solo permite claramente la separación de las pruebas, sino que también puede agregar semántica y claridad a lo que significan y representan las pruebas. Pero no existe una “talla única” en el software, tendrá que decidir qué enfoque le gusta en función de sus circunstancias particulares.

HTH!

Una solución muy sencilla es utilizar el -k argumento. Puedes usar el -k parámetro para anular la selección de determinadas pruebas. -k intenta hacer coincidir su argumento con cualquier parte del nombre o marcadores de las pruebas.Puede invertir la coincidencia utilizando not (también puede usar los operadores booleanos and y or). Por lo tanto -k 'not slow' omite las pruebas que tienen “lento” en el nombre, tiene un marcador con “lento” en el nombre, o cuyo nombre de clase / módulo contiene “lento”.

Por ejemplo, dado este archivo:

import pytest

def test_true():
    assert True

@pytest.mark.slow
def test_long():
    assert False

def test_slow():
    assert False

Cuando corres:

pytest -k 'not slow'

Produce algo como: (tenga en cuenta que ambas pruebas fallidas se omitieron porque coincidían con el filtro)

============================= test session starts =============================
platform win32 -- Python 3.5.1, pytest-3.4.0, py-1.5.2, pluggy-0.6.0
rootdir: c:UsersUserDocumentspython, inifile:
collected 3 items

test_thing.py .                                                          [100%]

============================= 2 tests deselected ==============================
=================== 1 passed, 2 deselected in 0.02 seconds ====================

Debido a la búsqueda de coincidencias, es posible que desee hacer algo como poner todas sus pruebas unitarias en un directorio llamado unittest y luego marcar los lentos como slow_unittest (para que coincida accidentalmente con una prueba que, por casualidad, tiene lento en el nombre). Entonces podrías usar -k 'unittest and not slow_unittest' para que coincida con todas sus pruebas unitarias rápidas.

Más ejemplos de uso de marcadores de Pytest

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *