【问题标题】:testing decorator with pytest using weird syntax使用奇怪的语法用 pytest 测试装饰器
【发布时间】:2017-10-28 01:08:35
【问题描述】:

所以我想测试一个装饰器,它可以为您提供一个已启动的协程(所以我不必这样做 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() 缺少 1 个必需的位置参数:'pattern'

然后我继续编辑第 2 行:

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

现在测试通过了。

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

所以在我的例子中 grep 接受一个参数;当我调用我的 grep 函数包装器时,我还需要给函数包装器一个参数,尽管它本身不接受任何参数?这就是装饰器的工作原理吗?

【问题讨论】:

  • 认为 pytest 固定装置在这里可能会造成混淆。当您将夹具作为参数传递给测试函数时,return 值就是参数。所以在你的情况下第 2 行 create_coroutine_func is grep.
  • @JacquesKvam 我可能错了,当我通过测试时,你的意思是“create_coroutine_func”是“grep”吗?我认为‘create_coroutine_func’的返回值会是‘grep’,不是吗?当你说返回值是参数时,这里的参数是什么?
  • create_coroutine_func(无括号)是grep,因为pytest在将其转换为夹具时调用了create_coroutine_func()(注意:括号)。
  • @phd 好的,我想我明白你的意思了。谢谢您的帮助!首先这有点令人困惑。
  • @JacquesKvam 谢谢我现在明白你的意思了。

标签: python decorator pytest coroutine


【解决方案1】:

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

这种对pytest fixtures的误解可能是test函数本身的值被误用的主要原因。

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


在这里,您在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 的模块导入(如果装饰函数在模块级别声明,就会发生这种情况,您可能已经注意到了)。

【讨论】:

  • 是的,我认为您的第一个解决方案看起来更清晰,就像我实际测试的那样,它也显示了每一步。对于第二个解决方案,但是我更喜欢将夹具放在 conftest 中,然后在我的测试中调用它。
  • 但第二个解决方案清楚地表明了我的测试意图。 :)
  • @stucash 欢迎您。不要忘记标记答案;-)
  • 非常高兴;但是您可以编辑您的答案以包括其他人的 cmets 吗?我确实认为这应该是我问题正确答案的重要组成部分。老实说,他们直接回答了我的问题:pytest 为我调用了它。你的第一段不是很清楚你的意思。:) 不过我很欣赏你的例子。非常感谢!
  • @stucash 啊!那不清楚?我确实错过了。好的,我在顶部添加了快速参考。
猜你喜欢
  • 1970-01-01
  • 2019-02-03
  • 2021-07-11
  • 1970-01-01
  • 1970-01-01
  • 2020-02-26
  • 1970-01-01
  • 2021-12-01
  • 2021-04-09
相关资源
最近更新 更多