pytest:以 DRY 方式参数化固定装置
pytest: parameterize fixtures in a DRY way
使用 Pytest fixtures,我正在寻找一种将设置覆盖传递给我的应用程序 fixtures 的方法,这样我就可以测试不同的设置而无需定义不同的 fixtures。
我在为 Flask 创建测试时使用的是一种常见模式,即初始化应用程序和数据库,如下所示。请注意,db
fixture 将 app
fixture 硬编码为参数。
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
装置?有没有一种方法可以在 测试用例 级别对夹具进行参数化,以便我可以将不同的设置传递给夹具?
即
# 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):
...
您可以尝试将设置作为字典参数传递给灯具,如下所示:
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
),这使得在没有附加参数的情况下使用测试成为可能。
使用 Pytest fixtures,我正在寻找一种将设置覆盖传递给我的应用程序 fixtures 的方法,这样我就可以测试不同的设置而无需定义不同的 fixtures。
我在为 Flask 创建测试时使用的是一种常见模式,即初始化应用程序和数据库,如下所示。请注意,db
fixture 将 app
fixture 硬编码为参数。
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
装置?有没有一种方法可以在 测试用例 级别对夹具进行参数化,以便我可以将不同的设置传递给夹具?
即
# 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):
...
您可以尝试将设置作为字典参数传递给灯具,如下所示:
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
),这使得在没有附加参数的情况下使用测试成为可能。