python - pytest:以 DRY 方式参数化装置

标签 python unit-testing flask pytest fixtures

使用 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 固定装置?有没有一种方法可以在测试用例级别对 fixture 进行参数化,以便我可以将不同的设置传递给 fixture ?

# 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 属性code> 对象,因此这本质上是对 fixture 本身进行参数化。

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

关于python - pytest:以 DRY 方式参数化装置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61330200/

相关文章:

Flask Admin - 创建新用户时自动创建密码哈希?

python - 替换 Pandas 数据框中所有出现的字符串(Python)

node.js - 模拟 Node.js 中测试的特定读取文件错误

ios - 如何监视 Kiwi 中的类方法?

php - 使用 app()->make 创建的模拟类

linux - Nginx 访问服务器上的文件时返回 404 not found

python - 当将 python 脚本作为 cron 选项卡运行时,如何启用本地模块?

python - 扭曲的 Python 暂停/推迟 react 器

python - 在 Python 中将整数 n 生成受限弱整数组合(或分区)为 k 部分

python - 使用 URL 处理器正确格式化规范的 URL 结构