简体   繁体   中英

testing decorator with pytest using weird syntax

so I wanted to test a decorator that gives you a coroutine that's primed up (so I don't have to do coroutine.send(None) ).

Details can be found here . David Beazley's example)

so in conftest.py I created a function wrapper that's a pytest fixture, which gives me a raw coroutine, if that makes sense.

the reason I am doing this is because pytest will take anything after yield keyword as teardown codes and I don't want that. so I wrapp up a raw coroutine myself.

@pytest.fixture(scope="module")
def create_coroutine_func():

    def grep(pattern):
        print "Looking for %s" % pattern
        while True:
            line = (yield)
            if pattern in line:
                print line
    return grep

now in my test file I have a rather simple test:

    from something.decorators import *
    import pytest

1    def test_coroutine(create_coroutine_func):
2        coro_func = create_coroutine_func()
3        coro_wrapper = coroutine(coro_func)
4    
5        assert callable(coro_wrapper)

ok that's it. pytest complained:

create_coroutine() missing 1 required positional argument: 'pattern'

then I went ahead edited line 2:

coro_func = create_coroutine_func("python") # match anything has 'python' in it.

now test passed.

now I a bit confused with decorators now.

So in my example grep takes an argument; when I call my grep function wrapper, I need to give the function wrapper an argument as well although it doesn't take any argument itself? is this how decorator works?

UPD: As @jacques-kvam pointed out in the comments, when you pass a fixture as an argument into a test function, the return value of the fixture IS the argument's value. So in your case, line 2 create_coroutine_func is grep .

This misunderstanding of the pytest fixtures is probably the main reason of misuse of the values in the test function itself.

Once this is clear, here is my original answer:


Here, you call your function on line 2 in create_coroutine_func() . This is why it expects the parameter to be passed.

What you actually need, if you want to wrap the function into a decorator, is to operate on the function-object itself. It do not call it, but pass it as a value to the decorator:

def test_coroutine(create_coroutine_func):
    coro_wrapper = coroutine(create_coroutine_func)
    assert callable(coro_wrapper)
    coro_wrapper("python")

This is how the decorators work: they take one function as an argument and return another (or the same) function as the result.


PS: This same test can be written in a more nice syntax:

from something.decorators import *
import pytest

def test_coroutine(create_coroutine_func):

    @coroutine
    def grep(pattern):
        print "Looking for %s" % pattern
        while True:
            line = (yield)
            if pattern in line:
                print line       

    assert callable(grep)
    grep("python")

Note that the decorator usage is inside of the test. So if anything goes wrong, the test will fail normally, not the pytest's module importing (as it would happen if the decorated function is declared on the module level, which you probably already noticed).

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