Swaroop Kundeti
Swaroop Kundeti

Reputation: 667

pytest two level of parametrization with one parameter dependent on other one

I have to a test a scenario where one parameter is dependent on other. I tried using the pytest hook pytest_generate_test but i'm not sure how to pass a retrieve the value in hook which is parametrized in the 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}")
    

Output:

============================= 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 ===============================

Expected output:

============================= 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 ===============================

I know I have hard coded to get the first element of the list service = apps[0] i'm not sure how can we get the parameter passed in test can be retrieved in pytest_generate_tests

To keep explain it in simple way

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')]

Upvotes: 0

Views: 64

Answers (2)

tmt
tmt

Reputation: 8654

Unless I missed some point in your question and all you want is a slightly odd product that you have already created in your mock example, then you should IMHO just use it as a helper function to generate the parameters for the parametrize decorator:

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}")

Upvotes: 1

Hai Vu
Hai Vu

Reputation: 40783

You almost solved your own problem. Here is one way to do it:

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)

Notes

  • The code between BEGIN sort and END sort ensures the order. If you don't care about the order (in general, you should not), then you can delete that part, which makes the script simpler to understand and shorter.
  • The custom_order translate dns to a1, dhcp to a2, which ensure the order you are looking for.

Upvotes: 0

Related Questions