Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
504 views
in Technique[技术] by (71.8m points)

python - Automatically wrap / decorate all pytest unit tests

Let's say I have a very simple logging decorator:

from functools import wraps

def my_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(f"{func.__name__} ran with args: {args}, and kwargs: {kwargs}")
        result = func(*args, **kwargs)
        return result
    return wrapper

I can add this decorator to every pytest unit test individually:

@my_decorator
def test_one():
    assert True

@my_decorator
def test_two():
    assert 1

How can I automatically add this decorator to every single pytest unit test so I don't have to add it manually? What if I want to add it to every unit test in a file? Or in a module?

My use case is to wrap every test function with a SQL profiler, so inefficient ORM code raises an error. Using a pytest fixture should work, but I have thousands of tests so it would be nice to apply the wrapper automatically instead of adding the fixture to every single test. Additionally, there may be a module or two I don't want to profile so being able to opt-in or opt-out an entire file or module would be helpful.

question from:https://stackoverflow.com/questions/65557740/automatically-wrap-decorate-all-pytest-unit-tests

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Provided you can move the logic into a fixture, as stated in the question, you can just use an auto-use fixture defined in the top-level conftest.py.

To add the possibility to opt out for some tests, you can define a marker that will be added to the tests that should not use the fixture, and check that marker in the fixture, e.g. something like this:

conftest.py

import pytest

def pytest_configure(config):
    config.addinivalue_line(
        "markers",
        "no_profiling: mark test to not use sql profiling"
    )

@pytest.fixture(autouse=True)
def sql_profiling(request):
    if not request.node.get_closest_marker("no_profiling"):
        # do the profiling
    yield

test.py

import pytest

def test1():
    pass # will use profiling

@pytest.mark.no_profiling
def test2():
    pass # will not use profiling

As pointed out by @hoefling, you could also disable the fixture for a whole module by adding:

pytestmark = pytest.mark.no_profiling

in the module. That will add the marker to all contained tests.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...