Search code examples
pythonunit-testingpytestdecorator

How to pass variables from a decorator with arguments to a pytest unit test?


Let's say I'm doing some unit tests with pytest that need some configuration. And let's say that I also want to add some custom configuration, depending on the unit test I'm trying to create.

So, I have currently the following:

import pytest

def load_configuration(custom_config=None):
    """Loads some default configuration, and if necessary injects some custom configuration"""
    config = some_complicated_stuff()
    if custom_config:
        config.update(custom_config)
    return config


@pytest.fixture(scope="function")
def foo():
    return 69


def test_bar_long_way(foo):
    config = load_configuration(custom_config={"bar": 42})
    assert foo == 69
    assert config[bar] == 42
    # do stuff with foo and config

Is there a way to inject that custom configuration into the unit test using a decorator (let's call it load_config), rather than having to create the configuration within the unit test itself? In this simplified example it's quite short, but in reality this takes a bit more space. I'm looking for a way to make it look like this:

@load_config({"bar": 42})
def test_bar_with_decorator(config, foo):
    assert foo == 69
    assert config["bar"] == 42
    # do stuff with foo and config

I can't figure out how to create this load_config decorator. Any help would be appreciated :).


Solution

  • import pytest
    
    
    def some_complicated_stuff():
        return {"abc": 123}
    
    
    def load_configuration(custom_config=None):
        """Loads some default configuration, and if necessary injects some custom configuration"""
        config = some_complicated_stuff()
        if custom_config:
            config.update(custom_config)
        return config
    
    
    @pytest.mark.parametrize('config', [load_configuration({"bar": 42})])
    def test_bar_long_way(config):
        assert config["bar"] == 42
        # do stuff with foo and config
    

    parametrize is usually used for running the same test function but given different values for its parameters, but we can use it to run only once.

    If you prefer having a nicer decorator :

    def custom_config(config_val):
        return pytest.mark.parametrize('config', [load_configuration(config_val)])
    
    
    @custom_config({"bar": 42})
    def test_bar_long_way(config):
        assert config["bar"] == 42