Search code examples
pythonmodulepytestfixtures

How to define a value in a pytest script at module level to be used in a fixture?


I'm trying to define a string in pytest script using a function at module level (and not directly assigning value to a variable) which will be used in a fixture with scope='module'. The problem is that when using a function at module level, it's being evaluated during the pytest collect phase - and when running pytest on a folder with a couple of scripts, the fixture uses the last evaluated value.

Is there an other way to set the string at module level, per script?

This is what I have so far, which didn't do the job:

my_script1.py:

import env_var as env

env.set_my_string('This string will be used in the module fixture')

test_first():
    # Do stuff

conf_test.py:

import pytest
import env_var as env

@pytest.fixture(scope='module', autouse=True)
def script_handler():
    txt = env.get_my_string()
    # Use txt
    yield

env_var.py:

_tmp_str = ''

def set_my_string(in_str)
    _tmp_str = in_str

def get_my_string():
    return _tmp_str

Solution

  • You can access the tested module in the fixture via the node object in the request fixture, as well as any variables or functions defined in that module:

    test_1.py

    my_env = "test1var"
    
    test_first():
        pass
    

    test_2.py

    my_env = "test2var"
    
    test_first():
        pass
    

    conftest.py

    @pytest.fixture(scope='module', autouse=True)
    def script_handler(request):
        txt = request.node.obj.my_var
        print(txt)
        yield
    

    request.node provides you with the test node, and obj is the module in the case of module level fixture, so you can just access the objects defined in the module via that variable.

    To illustrate - if you run this:

    $python -m pytest -v -s test_dir
    

    you can see that the module specific value is used:

    ...
    collected 2 items
    
    test_dir/test_1.py::test_first test1var
    PASSED
    test_dir/test_2.py::test_first test2var
    PASSED
    

    If for some reason you need to save the module-specific string elsewhere (as stated in the comments), you have to save them per module, for example by using the module name as key:

    env_var.py

    my_vars = {}
    
    def set_my_string(name, var):
        my_vars[name] = var
    
    def get_my_string(name):
        return my_vars.get(name, "some_default")
    

    test_1.py

    from env_var import set_my_string
    
    set_my_string(__name__, "test1var")
    
    def test_first():
        pass
    

    conftest.py

    import pytest
    
    from env_var import get_my_string
    
    
    @pytest.fixture(scope='module', autouse=True)
    def script_handler(request):
        module_name = request.node.obj.__name__
        print(get_my_string(module_name))
        yield