使用奇怪的语法用 pytest 测试装饰器
testing decorator with pytest using weird syntax
所以我想测试一个装饰器,它可以为您提供一个准备就绪的协程(所以我不必这样做 coroutine.send(None)
)。
可以找到详细信息here。大卫·比兹利的例子)
所以在 conftest.py 中,我创建了一个函数包装器,它是一个 pytest 夹具,如果有意义的话,它会给我一个原始协程。
我这样做的原因是因为 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() missing 1 required positional argument: 'pattern'
然后我继续编辑第 2 行:
coro_func = create_coroutine_func("python") # match anything has 'python' in it.
现已通过测试。
现在我对装饰器有点困惑了。
因此在我的示例中,grep 需要一个参数;当我调用我的 grep 函数包装器时,我还需要给函数包装器一个参数,尽管它本身不接受任何参数?装饰器是这样工作的吗?
UPD:正如@jacques-kvam 在评论中指出的那样,当您将夹具作为参数传递给测试函数时,夹具的 return 值就是参数的值。所以在你的例子中,第 2 行 create_coroutine_func
是 grep
.
这种对 pytest fixtures 的误解可能是误用测试函数本身的值的主要原因。
弄清楚后,这是我的原始答案:
在这里,您 在 create_coroutine_func()
的第 2 行调用 您的函数。这就是它希望传递参数的原因。
如果您想将函数包装到装饰器中,您实际需要的是对函数对象本身进行操作。它不调用它,而是将它作为值传递给装饰器:
def test_coroutine(create_coroutine_func):
coro_wrapper = coroutine(create_coroutine_func)
assert callable(coro_wrapper)
coro_wrapper("python")
装饰器是这样工作的:它们将一个函数作为参数,return 另一个(或相同的)函数作为结果。
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 的模块导入(如果修饰函数是在模块级别声明的,您可能已经注意到了,就会发生这种情况)。
所以我想测试一个装饰器,它可以为您提供一个准备就绪的协程(所以我不必这样做 coroutine.send(None)
)。
可以找到详细信息here。大卫·比兹利的例子)
所以在 conftest.py 中,我创建了一个函数包装器,它是一个 pytest 夹具,如果有意义的话,它会给我一个原始协程。
我这样做的原因是因为 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() missing 1 required positional argument: 'pattern'
然后我继续编辑第 2 行:
coro_func = create_coroutine_func("python") # match anything has 'python' in it.
现已通过测试。
现在我对装饰器有点困惑了。
因此在我的示例中,grep 需要一个参数;当我调用我的 grep 函数包装器时,我还需要给函数包装器一个参数,尽管它本身不接受任何参数?装饰器是这样工作的吗?
UPD:正如@jacques-kvam 在评论中指出的那样,当您将夹具作为参数传递给测试函数时,夹具的 return 值就是参数的值。所以在你的例子中,第 2 行 create_coroutine_func
是 grep
.
这种对 pytest fixtures 的误解可能是误用测试函数本身的值的主要原因。
弄清楚后,这是我的原始答案:
在这里,您 在 create_coroutine_func()
的第 2 行调用 您的函数。这就是它希望传递参数的原因。
如果您想将函数包装到装饰器中,您实际需要的是对函数对象本身进行操作。它不调用它,而是将它作为值传递给装饰器:
def test_coroutine(create_coroutine_func):
coro_wrapper = coroutine(create_coroutine_func)
assert callable(coro_wrapper)
coro_wrapper("python")
装饰器是这样工作的:它们将一个函数作为参数,return 另一个(或相同的)函数作为结果。
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 的模块导入(如果修饰函数是在模块级别声明的,您可能已经注意到了,就会发生这种情况)。