简体   繁体   中英

unable to capture results of pytest in decorator

my decorator for pytest tests exits the decorator as soon as the function is called. Works great if I run the file with python instead of pytest.

Here's the code:

def dec(func):
    def wrapper(*args, **kwargs):
        print('do some stuff')
        result = func(*args, **kwargs)
        print('ran function')
        return False if result else return True
    return wrapper

@dec
def test_something_is_not_true():
    return False # assert False

@dec
def test_something_is_true():
    return True # assert True


print(test_something_is_not_true())
print(test_something_is_true())

the results of this when I run the file with python are as follows:

C:\tests> python test.py
do some stuff
ran function
True
do some stuff
ran function
False

works great!

but when I run it with pytest it stops at the result = func(*args, **kwargs) line and never executes the print('ran function') line:

C:\tests>pytest test.py
============================= test session starts =============================
platform win32 -- Python 3.6.4, pytest-3.3.2, py-1.5.2, pluggy-0.6.0
rootdir: C:\, inifile:
plugins: metadata-1.7.0, jira-0.3.6, html-1.19.0
collected 2 items

test.py F.                                                               [100%]

================================== FAILURES ===================================
_________________________ test_something_is_not_true __________________________

args = (), kwargs = {}

    def wrapper(*args, **kwargs):
        print('do some stuff')
>       result = func(*args, **kwargs)

test.py:20:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

    @dec
    def test_something_is_not_true():
>       assert False
E       assert False

test.py:31: AssertionError
---------------------------- Captured stdout call -----------------------------
do some stuff
===================== 1 failed, 1 passed in 0.11 seconds ======================

As you can see, the decorator function is not really doing anything... but if I could get that to work then I could see if the test passed in that function and if so, perform some extra logic based on the result of the pass or fail of the test. Perhaps using Decorators to accomplish capture the output of a test isn't the best way to do it?

What would you do?

If you need to execute code before and after the test, this is done in a fixture . Example:

import pytest

@pytest.fixture
def wrapper(request):
    print('\nhere I am before the test')
    print('test function name is', request.node.name)
    print('test file is located under', request.node.fspath)
    yield
    print('\nand here I am after the test')


@pytest.mark.usefixtures('wrapper')
def test_spam_good():
    assert True

@pytest.mark.usefixtures('wrapper')
def test_spam_bad():
    assert False

As you can see, this is much more powerful than writing custom decorators. As pytest is very good at metaprogramming around tests, a lot of stuff you may need is already there, you just need to know how to access it. pytest docs contain many examples and recipes for beginners.


If you need the test outcome in the fixture, there is a recipe for that in the docs: Making test result information available in fixtures . Create a file conftest.py in the project root dir with the following content:

import pytest


@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
    # execute all other hooks to obtain the report object
    outcome = yield
    rep = outcome.get_result()

    # set a report attribute for each phase of a call, which can
    # be "setup", "call", "teardown"

    setattr(item, "rep_" + rep.when, rep)

Now you can access the test outcome via request fixture in your custom fixtures:

@pytest.fixture
def something(request):
    yield
    # request.node is an "item" because we use the default
    # "function" scope
    if request.node.rep_setup.failed:
        print("setting up a test failed!", request.node.nodeid)
    elif request.node.rep_setup.passed:
        if request.node.rep_call.failed:
            print("executing test failed", request.node.nodeid)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM