Python - использование pytest для пропуска теста, если не указано - PullRequest
0 голосов
/ 09 сентября 2018

Фон

Я использую pytest для тестирования веб-скребка, который помещает данные в базу данных. Класс только извлекает html и помещает html в базу данных для последующего анализа. Большинство моих тестов используют фиктивные данные для представления HTML.

Вопрос

Я хочу сделать тест, при котором веб-страница с веб-сайта очищается, но я хочу, чтобы тест автоматически отключался, если не указано иное. Аналогичный сценарий может быть, если у вас есть дорогой или трудоемкий тест, который вы не хотите всегда запускать.

Ожидаемое решение

Я ожидаю какой-нибудь маркер, который подавляет тест, если я не дам pytest для запуска всех подавленных тестов, но я не вижу этого в документации.

Что я сделал

  • В настоящее время я использую маркер пропуска и закомментирую его.
  • Попытался использовать маркер skipif и передать аргументы скрипту python, используя эту команду из командной строки pytest test_file.py 1 и следующий код ниже в тестовом файле. Проблема в том, что когда я пытаюсь предоставить аргумент в свой файл test_file, pytest ожидает, что это будет другое имя файла, поэтому я получаю сообщение об ошибке «тесты не выполняются в течение 0,00 секунд, ОШИБКА: файл не найден: 1»

    if len(sys.argv) == 1:
      RUN_ALL_TESTS = False
    else:
      RUN_ALL_TESTS = True
    ...
    # other tests
    ...
    
    @pytest.mark.skipif(RUN_ALL_TESTS)
    def test_scrape_website():
      ...
    
  • Возможно, я смогу обработать тест как фиксатор и использовать @pytest.fixture(autouse=False), но не знаю, как переопределить переменную autouse, хотя

  • Аналогичное решение было указано в Как пропустить pytest с использованием внешнего прибора? но это решение кажется более сложным, чем то, что мне нужно.

Ответы [ 3 ]

0 голосов
/ 09 сентября 2018

Есть несколько способов справиться с этим, но я рассмотрю два общих подхода, которые я видел в базовых версиях Python.

1) Разделите свои тесты, поместив «дополнительные» тесты в другой каталог.

Не уверен, как выглядит макет вашего проекта, но вы можете сделать что-то вроде этого (важен только тестовый каталог, остальное - просто пример макета игрушки):

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

Затем, когда вы вызываете pytest, вы вызываете его, выполняя pytest test/unit, если вы хотите запустить просто модульных тестов (т.е. только test_something*.py файлы), или pytest test/integration, если вы хотите запустите просто интеграционные тесты (т.е. только test_optional.py) или pytest test, если вы хотите запустить все тесты. Таким образом, по умолчанию вы можете просто запустить pytest unit/test.

Я рекомендую обернуть эти вызовы в какой-нибудь скрипт. Я предпочитаю make, так как он мощный для этого типа упаковки. Затем вы можете сказать make test, и он просто запустит ваш стандартный (быстрый) набор тестов, или make test_all, и он запустит все тесты (которые могут быть медленными или нет).

Пример Makefile, который вы можете обернуть:

.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) Разумно пометьте свои тесты с помощью декоратора @pytest.mark.skipif, но используйте переменную окружения в качестве триггера

Мне не очень нравится это решение, оно кажется мне бессистемным (трудно сказать, какой набор тестов выполняется при любом прогоне pytest). Однако вы можете определить переменную среды, а затем привязать эту переменную среды к модулю, чтобы определить, хотите ли вы выполнить все свои тесты. Переменные среды зависят от оболочки, но я буду притворяться, что у вас есть среда bash, поскольку это популярная оболочка.

Вы можете сделать export TEST_LEVEL="unit" только для быстрых юнит-тестов (так это будет по умолчанию) или export TEST_LEVEL="all" для всех ваших тестов. Затем в своих тестовых файлах вы можете делать то, что изначально пытались сделать, так:

import os

...

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

Примечание: Называть тестовые уровни "единица" и "интеграция" не имеет значения. Вы можете назвать их как хотите. Вы также можете иметь много уровней (например, ночные тесты или тесты производительности).

Кроме того, я думаю, что вариант 1 - лучший путь, поскольку он не только четко позволяет разделить тестирование, но также может добавить семантику и ясность к тому, что тесты означают и представляют. Но в программном обеспечении не существует «одного размера для всех», вам придется решать, какой подход вам больше подходит, исходя из ваших конкретных обстоятельств.

НТН!

0 голосов
/ 10 сентября 2018

Очень простое решение - использовать аргумент -k. Вы можете использовать параметр -k, чтобы отменить выбор некоторых тестов. -k пытается сопоставить свой аргумент с любой частью имени или маркера теста. Вы можете инвертировать совпадение с помощью not (вы также можете использовать логические операторы and и or). Таким образом, -k 'not slow' пропускает тесты, в названии которых указано "slow", имеет маркер с именем "slow" или имя класса / модуля которого содержит "slow".

Например, для данного файла:

import pytest

def test_true():
    assert True

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

def test_slow():
    assert False

При запуске:

pytest -k 'not slow'

Выводит что-то вроде: (обратите внимание, что оба неудачных теста были пропущены, поскольку они соответствовали фильтру)

============================= test session starts =============================
platform win32 -- Python 3.5.1, pytest-3.4.0, py-1.5.2, pluggy-0.6.0
rootdir: c:\Users\User\Documents\python, inifile:
collected 3 items

test_thing.py .                                                          [100%]

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

Из-за нетерпеливого сопоставления вы можете захотеть сделать что-то вроде помещения всех ваших юнит-тестов в каталог с именем unittest и затем пометить медленные как slow_unittest (чтобы случайно совпадать с тестом, который так случается с медленно в названии). Затем вы можете использовать -k 'unittest and not slow_unittest', чтобы соответствовать всем вашим быстрым модульным тестам.

Больше примеров использования маркера Pytest

0 голосов
/ 09 сентября 2018

Документы точно описывают вашу проблему: https://docs.pytest.org/en/latest/example/simple.html#control-skipping-of-tests-according-to-command-line-option. Копирование оттуда:

Вот файл conftest.py, добавляющий параметр командной строки --runslow к контроль пропуска тестов с пометкой 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)

Теперь мы можем написать тестовый модуль так:

# content of test_module.py
import pytest


def test_func_fast():
    pass


@pytest.mark.slow
def test_func_slow():
    pass
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...