Pytest:摆脱装饰器引入的测试函数中使用的冗余代码

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

我得到了以下非常简单的函数和测试:

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"

我知道可能有一些方法可以将这些测试减少为使用夹具或 pytest.mark.parametrize 的单个测试,但我不知道如何解决这个问题。有谁知道如何用装饰器做到这一点?

“手动”将装饰器应用于函数,而不是使用装饰器语法糖。然后你可以在一个循环中完成所有三个测试。

from feedback import error_msg, info_msg, warn_msg
import pytest

MESSAGE_BY_DECORATOR = [
(info_msg, "INFO"),
(error_msg, "ERROR"),
(warn_msg, "WARN")]


def test_message_all(capsys):

    def message():
        return "testmessage"

    for decorator, expected in MESSAGE_BY_DECORATOR:
        decorated_message = decorator(message)
        decorated_message()
        assert capsys.readouterr().out == f"{expected}: testmessage\n"
        # you'll need to reset your capsys object here

我会使用 @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

(已编辑以使其可运行,并修复了一些错误。)