【问题标题】:pytest: parameterize fixtures in a DRY waypytest:以 DRY 方式参数化夹具
【发布时间】:2020-04-20 19:11:44
【问题描述】:

使用 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() 函数。在每个测试的基础上,如何附加 appdb 夹具,以便我可以将设置覆盖传递给 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):
    ...


【问题讨论】:

    标签: python unit-testing flask pytest fixtures


    【解决方案1】:

    您可以尝试将设置作为字典参数传递给夹具,如下所示:

    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 对象使夹具能够访问请求的测试上下文,并可用作任何夹具中的参数。
    pytest.mark.parametrize 装饰器中的 indirect=True 参数将参数传递给 request 对象的可选 param 属性,因此这实质上是对夹具本身进行参数化。

    更新:
    我添加了@JoeJ 提出的有用的补充(hasattr 的用法),这使得可以在没有附加参数的情况下使用测试。

    【讨论】:

    • 感谢您的解决方案。这正是我希望做的。我不知道那个间接参数或请求夹具。
    • 很高兴我能帮上忙!我更新了答案以包含您的更改,因为它们通常很有帮助 - 我错过了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-03
    • 1970-01-01
    • 2022-01-21
    • 1970-01-01
    • 2022-12-08
    • 1970-01-01
    • 2015-11-23
    相关资源
    最近更新 更多