Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.2k views
in Technique[技术] by (71.8m points)

python - How to use pytest capsys on tests that have mocking decorators?

I have being trying to find a way to use mocking decorators and pytest capsys at the same time but I wasn't able to find the right way to do it.

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?
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

As stated in the request-mock's docs:

pytest has its own method of registering and loading custom fixtures. requests-mock provides an external fixture registered with pytest such that it is usable simply by specifying it as a parameter. There is no need to import requests-mock it simply needs to be installed and specify the argument requests_mock.

The fixture then provides the same interface as the requests_mock.Mocker letting you use requests-mock as you would expect.

>>> 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
...

So just use the requests_mock fixture instead of the decorator:

def test_with_mock_and_capsys(requests_mock, capsys):
    pass

Background

pytest doesn't play along with function decorators that add positional arguments to the test function. pytest considers all arguments that

  • aren't bound to an instance or type as in instance or class methods;
  • don't have default values;
  • aren't bound with functools.partial;
  • aren't replaced with unittest.mock mocks

to be replaced with fixture values, and will fail if it doesn't find a suitable fixture for any argument. So stuff like

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

will fail, and this is exactly what requests-mock does. A workaround to that would be passing the mocker via keyword args:

import pytest
import requests_mock


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

but since requests-mock already offers a fixture, why bother using the decorator?


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...