Search code examples
pythonpytestpython-decorators

Pytest: Get rid of redudant Code used in testfunctions introduced by decorators


I have got the following very simple functions and tests:

from feedback import error_msg, info_msg, warn_msg
import pytest


def test_message_info(capsys):

    @info_msg
    def message():
        return "testmessage"
    message()

    assert capsys.readouterr().out == "INFO: testmessage\n"



def test_message_error(capsys):

    @error_msg
    def message():
        return "testmessage"
    message()

    assert capsys.readouterr().out == "ERROR: testmessage\n"


def test_message_warning(capsys):

    @warn_msg
    def message():
        return "testmessage"
    message()

    assert capsys.readouterr().out == "WARN: testmessage\n"

I know there are probably ways reduce these tests to a single test with a fixture or pytest.mark.parametrize, but I can not get my head around how to solve this. Does anyone know how to do this with a decorator?


Solution

  • I'd use @pytest.mark.parametrize:

    import pytest
    import functools
    
    # Quickly reimplement the `feedback` decorators, so that this
    # example will be runnable.  These lines aren't important if
    # you're just interested in the answer to the question at hand.
    
    def log_msg(message):
    
        def decorate(f):
    
            @functools.wraps(f)
            def wrapper(*args, **kwargs):
                out = f(*args, **kwargs)
                print(f'{message}: {out}')
    
            return wrapper
    
        return decorate
    
    info_msg = log_msg('INFO')
    error_msg = log_msg('ERROR')
    warn_msg = log_msg('WARN')
    
    # Make a parametrized test:
    
    @pytest.mark.parametrize(
            'decorator, expected', [
                (info_msg, 'INFO: testmessage\n'),
                (error_msg, 'ERROR: testmessage\n'),
                (warn_msg, 'WARN: testmessage\n'),
            ],
    )
    def test_decorators(decorator, expected, capsys):
    
        @decorator
        def message():
            return "testmessage"
    
        message()
    
        assert capsys.readouterr().out == expected
    

    (Edited to make runnable, and to fix some bugs.)