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

sor*_*rin 3 python testing mocking pytest python-decorators

我一直在尝试找到一种同时使用模拟装饰器和 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?
Run Code Online (Sandbox Code Playgroud)

hoe*_*ing 6

正如request-mock文档中所述

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

该装置然后提供与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
...
Run Code Online (Sandbox Code Playgroud)

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

def test_with_mock_and_capsys(requests_mock, capsys):
    pass
Run Code Online (Sandbox Code Playgroud)

背景

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
Run Code Online (Sandbox Code Playgroud)

会失败,而这正是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']
    ...
Run Code Online (Sandbox Code Playgroud)

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

  • 大家好,我是 requests-mock 的作者。感谢您的回答,从现在开始我将把人们与此联系起来。您介意我将背景片段批量复制到文档中吗? (2认同)