12

I have a test that is parametrized with @pytest.mark.parametrize so that the same test function can be executed with different parameters. @pytest.mark.parametrize uses the ids argument defined as a function so that a custom test ID is generated for each parameter.

My test function writes a file to disk that tabulates the differences between actual and expected results for the given parameter. I would like to report the test ID in the file.

Is there an API I can call which will tell me what test ID applies to a particular execution of the test?

2

2 Answers 2

11

You can use request.node.callspec.id from the Request Fixture.


But take note that the value depends on whether the ids function "generates a string representation to make part of the test ID". From pytest's docs on Different options for test IDs, the stringified version of the param is normally added to the test ID.

For example, if the ids function generates an ID that does not include the stringified param or does not depend on anything else:

import random
def generate_random_id(param) -> str:
    return f'{random.randint(1,100)}'

testdata = ['argA', 'argB']

@pytest.mark.parametrize('param', testdata, ids=generate_random_id)
def test_foo(param, request):
    print(request.node.callspec.id)

Then you would get the generated ID as-is:

tests/test_a.py::test_foo[78] PASSED      
tests/test_a.py::test_foo[10] PASSED      

============================ PASSES =============================
_________________________ test_foo[78] __________________________
--------------------- Captured stdout call ----------------------
78
_________________________ test_foo[10] __________________________
--------------------- Captured stdout call ----------------------
10

If the ids function includes the string representation of the param, then you would need some sort of delimiter to manually split the param and the test ID:

import random
def generate_random_id(param):
    test_id = random.randint(1, 100)
    return f'{param}-{test_id}'  

testdata = ['argA', 'argB']

@pytest.mark.parametrize('param', testdata, ids=generate_random_id)
def test_foo(param, request):
    param, test_id = request.node.callspec.id.split('-')
    print(param)
    print(test_id)
tests/test_a.py::test_foo[argA-43] PASSED 
tests/test_a.py::test_foo[argB-37] PASSED 

============================ PASSES =============================
_______________________ test_foo[argA-43] _______________________
--------------------- Captured stdout call ----------------------
argA
43
_______________________ test_foo[argB-37] _______________________
--------------------- Captured stdout call ----------------------
argB
37

If there are multiple params, then it becomes more complicated, since all the param + ids combinations are concatenated together to create the full Test ID. You will need a separate delimiter different from -:

import random
def generate_random_id(param):
    test_id = random.randint(1, 100)
    return f'{param}+{test_id}'  # Use '+' to separate param+ID

testdata = [
    ('argA', 'argB'),
    ('argC', 'argD'),
]

@pytest.mark.parametrize('param1, param2', testdata, ids=generate_random_id)
def test_foo(param1, param2, request):
    print(request.node.callspec.id)
    params = request.node.callspec.id.split('-')
    for a_param in params:
        param, test_id = a_param.split('+')
        print(param)
        print(test_id)
tests/test_a.py::test_foo[argA+70-argB+25] PASSED         
tests/test_a.py::test_foo[argC+97-argD+30] PASSED          

===================================== PASSES =====================================
___________________________ test_foo[argA+70-argB+25] ____________________________
------------------------------ Captured stdout call ------------------------------
argA+70-argB+25
argA
70
argB
25
___________________________ test_foo[argC+97-argD+30] ____________________________
------------------------------ Captured stdout call ------------------------------
argC+97-argD+30
argC
97
argD
30
1

You can use:

os.environ.get('PYTEST_CURRENT_TEST')

To get the current parametrize execution.

A similar question was already asked and has a lot of other options: py.test: how to get the current test's name from the setup method?

If you are looking for a more dynamic way of doing it, take a look in: https://hackebrot.github.io/pytest-tricks/param_id_func/, is a plugin for pytest that may easy the pain.

Not the answer you're looking for? Browse other questions tagged or ask your own question.