pytest: параметризация приборов DRY способом - PullRequest
2 голосов
/ 20 апреля 2020

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

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

from myapp import create_app

@pytest.fixture
def app():
    settings_override = {}  # By setting values here, I can pass in different Flask config variables
    app = create_app(settings_override)
    return app

@pytest.fixture
def db(app):
    do_something_to_create_the_database(app)  # app needed for context
    yield db

А затем в ряде тестов могут использоваться определенные выше приборы, такие как.

def test_my_application_1(db, app):
  ...

def test_my_application_2(db, app):
  ...

Допустим, я хочу инициализировать фикстуру приложения с другими настройками, и допустим, что я могу передать эти настройки в функцию create_app (), определенную выше. Как для каждого теста можно прикрепить приборы app и db таким образом, чтобы я мог передать переопределения настроек к устройству app? Есть ли способ, которым я могу параметризовать прибор на уровне test case , чтобы я мог передать различные настройки на прибор?

т.е.

# for this test, I want to pass the BAZ=True setting to the app fixture. 
def test_my_application_1(db, app):
  ...

# for this test, I want to pass FOO=BAR setting to the app fixture
def test_my_application_2(db, app):
  ...

Я ценю любые советы, которые вы можете предложить.

Обновление: с решением от @ mrbean-bremen

Спасибо @MrBean Bremen за элегантное решение. С небольшим изменением, используя hasattr, я смог расширить решение, чтобы принимать переопределения параметров или принимать значения по умолчанию.

@pytest.fixture(scope='function')
def app(request):
    settings_override = {
        'SQLALCHEMY_DATABASE_URI': "sqlite:///:memory:",
    }
    params = request.param if hasattr(request, 'param') else {}
    return create_app({**settings_override, **params})


@pytest.fixture(scope='function')
def db(app):
    with app.app_context():
       ....


def test_without_params(db, app):
    ...


@pytest.mark.parametrize("app", [{'DEBUG': True}], indirect=True)
def test_with_overrides(db, app):
    ...


1 Ответ

2 голосов
/ 20 апреля 2020

Вы можете попытаться передать настройки в качестве параметра словаря в прибор, что-то вроде этого:

import pytest
from myapp import create_app

@pytest.fixture
def app(request):
    settings_override = {
        'SQLALCHEMY_DATABASE_URI': "sqlite:///:memory:",
    }
    params = request.param if hasattr(request, 'param') else {}
    return create_app({**settings_override, **params})

@pytest.fixture
def db(app):
    do_something_to_create_the_database(app)
    yield db

def test_my_application_no_override_params(db, app):
    ...

@pytest.mark.parametrize("app", [{'BAZ': True}], indirect=True)
def test_my_application_1(db, app):
    ...

@pytest.mark.parametrize("app", [{'FOO': 'BAR'}], indirect=True)
def test_my_application_2(db, app):
    ...

Объект request предоставляет доступ к устройству в запрашивающем тестовом контексте и может использоваться как аргумент в любом приборе.
Аргумент indirect=True в декораторе pytest.mark.parametrize передает параметр в необязательный атрибут param объекта request, так что это по существу параметризует сам прибор.

ОБНОВЛЕНИЕ:
Я добавил полезное дополнение (использование hasattr), предложенное @JoeJ, которое позволяет использовать тест без дополнительных параметров.

...