pytest 两级参数化,参数间相互依赖
我需要测试一个场景,其中一个参数依赖于另一个参数。我尝试使用pytest的一个钩子函数pytest_generate_test
,但我不确定如何在这个钩子中获取测试中传递的参数值。
import pytest
import logging
logger = logging.getLogger(__name__)
apps = ['app1', 'app2', 'app3']
def pytest_generate_tests(metafunc):
common_services = ['dns', 'dhcp']
service = apps[0]
common_services.append(service)
if "total_services" in metafunc.fixturenames:
metafunc.parametrize("total_services", common_services)
@pytest.fixture()
def total_services(request):
return request.param
@pytest.mark.parametrize("app", apps)
def test_example(app, total_services):
logging.info(f"App: {app}, ServiceName: {total_services}")
输出结果:
============================= test session starts ==============================
platform darwin -- Python 3.11.7, pytest-8.0.2, pluggy-1.4.0
rootdir: /private/tmp/test/tests
collected 9 items
test_example.py::test_example[dns-app1]
-------------------------------- live log call ---------------------------------
INFO root:test_example.py:21 App: app1, ServiceName: dns
PASSED [ 11%]
test_example.py::test_example[dns-app2]
-------------------------------- live log call ---------------------------------
INFO root:test_example.py:21 App: app2, ServiceName: dns
PASSED [ 22%]
test_example.py::test_example[dns-app3]
-------------------------------- live log call ---------------------------------
INFO root:test_example.py:21 App: app3, ServiceName: dns
PASSED [ 33%]
test_example.py::test_example[dhcp-app1]
-------------------------------- live log call ---------------------------------
INFO root:test_example.py:21 App: app1, ServiceName: dhcp
PASSED [ 44%]
test_example.py::test_example[dhcp-app2]
-------------------------------- live log call ---------------------------------
INFO root:test_example.py:21 App: app2, ServiceName: dhcp
PASSED [ 55%]
test_example.py::test_example[dhcp-app3]
-------------------------------- live log call ---------------------------------
INFO root:test_example.py:21 App: app3, ServiceName: dhcp
PASSED [ 66%]
test_example.py::test_example[app1-app1]
-------------------------------- live log call ---------------------------------
INFO root:test_example.py:21 App: app1, ServiceName: app1
PASSED [ 77%]
test_example.py::test_example[app1-app2]
-------------------------------- live log call ---------------------------------
INFO root:test_example.py:21 App: app2, ServiceName: app1
PASSED [ 88%]
test_example.py::test_example[app1-app3]
-------------------------------- live log call ---------------------------------
INFO root:test_example.py:21 App: app3, ServiceName: app1
PASSED [100%]
============================== 9 passed in 0.01s ===============================
预期的输出结果:
============================= test session starts ==============================
platform darwin -- Python 3.11.7, pytest-8.0.2, pluggy-1.4.0
rootdir: /private/tmp/test/tests
collected 9 items
test_example.py::test_example[dns-app1]
-------------------------------- live log call ---------------------------------
INFO root:test_example.py:21 App: app1, ServiceName: dns
PASSED [ 11%]
test_example.py::test_example[dns-app2]
-------------------------------- live log call ---------------------------------
INFO root:test_example.py:21 App: app2, ServiceName: dns
PASSED [ 22%]
test_example.py::test_example[dns-app3]
-------------------------------- live log call ---------------------------------
INFO root:test_example.py:21 App: app3, ServiceName: dns
PASSED [ 33%]
test_example.py::test_example[dhcp-app1]
-------------------------------- live log call ---------------------------------
INFO root:test_example.py:21 App: app1, ServiceName: dhcp
PASSED [ 44%]
test_example.py::test_example[dhcp-app2]
-------------------------------- live log call ---------------------------------
INFO root:test_example.py:21 App: app2, ServiceName: dhcp
PASSED [ 55%]
test_example.py::test_example[dhcp-app3]
-------------------------------- live log call ---------------------------------
INFO root:test_example.py:21 App: app3, ServiceName: dhcp
PASSED [ 66%]
test_example.py::test_example[app1-app1]
-------------------------------- live log call ---------------------------------
INFO root:test_example.py:21 App: app1, ServiceName: app1
PASSED [ 77%]
test_example.py::test_example[app1-app2]
-------------------------------- live log call ---------------------------------
INFO root:test_example.py:21 App: app2, ServiceName: app2
PASSED [ 88%]
test_example.py::test_example[app1-app3]
-------------------------------- live log call ---------------------------------
INFO root:test_example.py:21 App: app3, ServiceName: app3
PASSED [100%]
============================== 9 passed in 0.01s ===============================
我知道我硬编码了来获取列表中的第一个元素service = apps[0]
,但我不确定如何在pytest_generate_tests
中获取测试中传递的参数。
为了简单明了地解释这个问题:
from itertools import product
apps = ['app1', 'app2', 'app3']
def cartesian_product(app, common_servcies):
return list(product(app, common_servcies))
for app in apps:
common_services = ['dns', 'dhcp']
common_services.append(app)
print(cartesian_product([app], common_services))
Output:
$ python3 test_example2.py
[('app1', 'dns'), ('app1', 'dhcp'), ('app1', 'app1')]
[('app2', 'dns'), ('app2', 'dhcp'), ('app2', 'app2')]
[('app3', 'dns'), ('app3', 'dhcp'), ('app3', 'app3')]
2 个回答
0
你几乎自己解决了问题。这是一个可以做到的方法:
import itertools
import logging
import pytest
SERVICES_AND_APPS = []
for app in ["app1", "app2", "app3"]:
services = ["dns", "dhcp"] + [app]
SERVICES_AND_APPS.extend(itertools.product(services, [app]))
# BEGIN sort
def custom_key(service_and_app):
"""Ensure sort order of dns, dhcp, app1, app2, app3."""
custom_order = {
"dns": "a1",
"dhcp": "a2",
}
service, app = service_and_app
return custom_order.get(service, service), app
SERVICES_AND_APPS.sort(key=custom_key)
# END sort
@pytest.mark.parametrize("service,app", SERVICES_AND_APPS)
def test_example(service, app):
logging.info("app=%r, service=%r", app, service)
注意事项
- 在
BEGIN sort
和END sort
之间的代码是用来确保顺序的。如果你不在乎顺序(一般来说,你不需要在乎),那么可以把那部分删掉,这样脚本会更简单易懂,也会更短。 custom_order
把dns
翻译成a1
,把dhcp
翻译成a2
,这样就能确保你想要的顺序。
1
除非我在你的问题中漏掉了什么重点,或者你只是想要一个在你示例中看起来有点奇怪的产品,那么在我看来,你可以把它当作一个辅助函数来使用,用来生成parametrize
装饰器的参数:
import logging
from itertools import product
import pytest
def app_service_product(apps, common_services):
return list(product(apps, common_services)) + [[a, a] for a in apps]
@pytest.mark.parametrize(
"app, service",
app_service_product(["app1", "app2", "app3"], ["dns", "dhcp"]),
)
def test_example(app, service):
logging.info(f"App: {app}, ServiceName: {service}")