如何在具有模拟装饰器的测试中使用 pytest capsys?

Posted

技术标签:

【中文标题】如何在具有模拟装饰器的测试中使用 pytest capsys?【英文标题】:How to use pytest capsys on tests that have mocking decorators? 【发布时间】:2019-02-03 06:27:33 【问题描述】:

我一直在尝试找到一种同时使用模拟装饰器和 pytest capsys 的方法,但我找不到正确的方法。

import pytest
import requests_mock


@requests_mock.mock()
def test_with_mock(m):
    pass

def test_with_capsys(capsys):
    pass


# how to write a test that works with both?

【问题讨论】:

【参考方案1】:

request-mock's docs中所述:

pytest 有自己的注册和加载自定义设备的方法。 requests-mock 提供了一个用pytest 注册的外部夹具,因此只需将其指定为参数即可使用。无需导入requests-mock,只需安装并指定参数requests_mock即可。

然后,fixture 提供与requests_mock.Mocker 相同的接口,让您可以按预期使用requests-mock

>>> import pytest
>>> import requests

>>> def test_url(requests_mock):
...     requests_mock.get('http://test.com', text='data')
...     assert 'data' == requests.get('http://test.com').text
...

所以只需使用requests_mock 夹具而不是装饰器:

def test_with_mock_and_capsys(requests_mock, capsys):
    pass

背景

pytest 不能与向测试函数添加位置参数的函数装饰器一起使用。 pytest 考虑所有参数,

不像在实例或类方法中那样绑定到实例或类型; 没有默认值; 不与functools.partial绑定; 不替换为 unittest.mock 模拟

被替换为夹具值,如果没有找到适合任何参数的夹具,则会失败。所以像

import functools
import pytest


def deco(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        args += ('spam',)
        return func(*args, **kwargs)
    return wrapper


@deco
def test_spam(spam_arg):
    assert True

会失败,这正是requests-mock 所做的。一种解决方法是通过关键字 args 传递 mocker:

import pytest
import requests_mock


@requests_mock.Mocker(kw='m')
def test_with_mock_and_fixtures(capsys, **kwargs):
    m = kwargs['m']
    ...

但是既然requests-mock已经提供了一个fixture,为什么还要使用装饰器呢?

【讨论】:

大家好,我是 requests-mock 的作者。感谢您的回答,我将从现在开始将人们链接到此。你介意我只是将背景 sn-p 复制到文档中吗? 当然,很高兴这个例子有帮助!

以上是关于如何在具有模拟装饰器的测试中使用 pytest capsys?的主要内容,如果未能解决你的问题,请参考以下文章

如何通过自定义装饰器提供非夹具 pytest 参数?

如何在 Python 中模拟装饰器的内部调用

pytest运行方式及装饰器的使用

具有装饰器的模拟功能。再次使用相同的装饰器来装饰 Mock 对象并使其保持为 Mock

如何绕过 pytest 夹具装饰器?

pytest 参数化升华版