使用奇怪的语法使用pytest测试装饰器

时间:2017-10-28 01:08:36

标签: python decorator pytest coroutine

所以我想测试一个装饰器,它会给你一个已经准备好的协同程序(所以我不必做coroutine.send(None))。

可以找到详细信息here。 David Beazley的例子)

所以在conftest.py中我创建了一个函数包装器,它是一个pytest fixture,如果有意义的话,它会给我一个原始协程。

我之所以这样做,是因为pytest会将yield关键字作为拆解代码,并且我不想这样做。所以我自己包装了一个原始的协程。

@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

现在在我的测试文件中我有一个相当简单的测试:

    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)
好吧,那就是它。 pytest抱怨:

  

create_coroutine()缺少1个必需的位置参数:' pattern'

然后我继续编辑第2行:

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

现在测试通过了。

现在我对装饰师有点困惑。

所以在我的例子中,grep接受了一个论点;当我调用我的grep函数包装器时,我还需要为函数包装器提供一个参数,尽管它本身并不接受任何参数?这是装饰师的工作方式吗?

1 个答案:

答案 0 :(得分:1)

UPD:正如@ jacques-kvam在评论中指出的那样,当你将一个灯具作为参数传递给一个测试函数时,灯具的返回值就是参数的值。因此,在您的情况下,第2行create_coroutine_funcgrep

这种对pytest灯具的误解可能是误用测试功能本身的价值的主要原因。

一旦清楚,这是我原来的答案:

在这里,您create_coroutine_func()的第2行调用您的功能。这就是为什么它希望传递参数。

如果要将函数包装到装饰器中,实际需要的是对函数对象本身进行操作。它不会调用它,而是将其作为值传递给装饰器:

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

这就是装饰器的工作方式:它们将一个函数作为参数,并返回另一个(或相同的)函数。

PS:同样的测试可以用更好的语法编写:

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")

请注意,装饰器用法是在测试内部。因此,如果出现任何问题,测试将正常失败,而不是pytest的模块导入(因为如果在模块级别声明了修饰函数,就会发生这种情况,你可能已经注意到了)。