我可以用其他装置参数化 pytest 装置吗?

问题描述 投票:0回答:2

我有一个 python 测试,它使用固定装置作为凭据(用户 ID 和密码的元组)

def test_something(credentials)
   (userid, password) = credentials
   print("Hello {0}, welcome to my test".format(userid))

我有用于凭证的 pytest 固定装置:

@pytest.fixture()
def credentials():
   return ("my_userid", "my_password")

效果很好

现在我想将其扩展到多个凭据(例如登台和生产),以便我的测试将运行两次(登台和生产各一次)。

我以为参数化就是答案,但似乎我无法用夹具进行参数化。

我很想做这样的事情:

@pytest.fixture(params=[staging_credentials, production_credentials])
def credentials(request):
    return request.param

其中 staging_credentials 和 production_credentials 都是固定装置:

@pytest.fixture()
def staging_credentials():
   return ("staging_userid", "staging_password")

@pytest.fixture()
def production_credentials():
   return ("prod_userid", "prod_password")

但显然该灯具的参数不能是其他灯具。

关于如何优雅地处理这个问题有什么建议吗? 我看过https://docs.pytest.org/en/latest/proposals/parametrize_with_fixtures.html但这似乎太暴力了。

谢谢! 史蒂夫

python pytest fixtures
2个回答
10
投票

间接参数化就是答案。因此,灯具的参数可以是其他灯具(按名称/代码)。

import pytest

all_credentials = {
    'staging': ('user1', 'pass1'),
    'prod': ('user2', 'pass2'),
}

@pytest.fixture
def credentials(request):
    return all_credentials[request.param]

@pytest.mark.parametrize('credentials', ['staging', 'prod'], indirect=True)
def test_me(credentials):
    pass

从技术上讲,你不仅可以通过其键获取字典值,还可以根据

credentials
生成
request.param
结果,并且此参数将是传递给测试的同名参数的值。

如果您想使用其他灯具(可能是因为安装/拆卸阶段,因为这是这样做的唯一原因):

import pytest

@pytest.fixture
def credentials(request):
    return request.getfuncargvalue(request.param)

@pytest.fixture()
def staging_credentials():
   return ("staging_userid", "staging_password")

@pytest.fixture()
def production_credentials():
   return ("prod_userid", "prod_password")

@pytest.mark.parametrize('credentials', ['staging_credentials', 'production_credentials'], indirect=True)
def test_me(credentials):
    pass

这里,

request.getfuncargvalue(...)
将通过灯具名称动态返回灯具值。


0
投票

这是使用我创建的新 pytest 插件 pytest-fixture-forms 的更简单的解决方案:

from pytest_fixture_forms import FixtureForms
import pytest

class Credentials(FixtureForms):
    @pytest.fixture
    def staging(self):
        return ("staging_userid", "staging_password")
    
    @pytest.fixture
    def production(self):
        return ("prod_userid", "prod_password")

def test_something(credentials):
    userid, password = credentials.value
    print(f"Hello {userid}, welcome to my test")

虽然间接参数化有效,但与使用 pytest-fixture-forms 相比,它有几个缺点:

  1. 可读性:间接方法需要了解 pytest 的间接参数化,这可能会让新手感到困惑。使用固定形式,您只需在类中编写普通固定装置 - 就可以立即清楚每个凭证变体的作用。
  2. 维护:使用间接参数化,您需要维护测试装饰器中的夹具定义和参数列表。如果您添加新的凭据类型,则必须记住更新这两个位置。使用固定形式,您只需向类添加一个新方法,一切都会自动运行。
  3. 表单意识:间接方法无法让您轻松访问正在使用的凭证类型。使用固定形式,您可以访问
    credentials.form
    了解您是否正在使用“登台”或“生产”凭证进行测试:
def test_something(credentials):
    userid, password = credentials.value
    if credentials.form == "staging":
        assert userid.startswith("staging_")
  1. 组织:Fixture-forms 将相关变体分组到一个类中,使代码结构更直观、更易于导航。间接方法将这些信息传播到多个装置和测试装饰器中。

如果您只想使用特定凭据运行测试(就像间接示例那样),您仍然可以这样做:

@pytest.mark.parametrize("credentials_form", ["staging"])  # Only test staging
def test_something(credentials):
    userid, password = credentials.value

该插件在内部处理所有复杂性,同时为您的测试提供更简单、更直观的 API。

安装插件

pip install pytest-fixture-forms

© www.soinside.com 2019 - 2024. All rights reserved.