Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

requests-mock decorator not recognised in Python 3 #2749

Closed
volans- opened this issue Sep 2, 2017 · 17 comments
Closed

requests-mock decorator not recognised in Python 3 #2749

volans- opened this issue Sep 2, 2017 · 17 comments
Labels
status: needs information reporter needs to provide more information; can be closed after 2 or more weeks of inactivity

Comments

@volans-
Copy link

volans- commented Sep 2, 2017

Error

When running the following code with pytest, it passes on Python 2.7 but fails on Python 3.6 with E fixture 'mocked_obj' not found:

import requests_mock


@requests_mock.Mocker()
def test_foo(mocked_obj):
    pass


@requests_mock.Mocker()
class TestFoo(object):

    def test_foo(self, mocked_obj):
        pass

It seems to me that pytest is not recognising the mocked_obj parameter as the one already passed by the decorator hence looks for a fixture with that name and fail.

I'm unsure if the different behaviour between Python 2 and Python 3 is in pytest or mock-requests modules (or both). If you are confident that it is not related to pytest let me know and I'll open the issue for the mock-requests project.

Workaround

If I change the function/method signatures replacing mocked_obj with *args it works as expected: the args tuple has only one element and is an instance of requests_mock.mocker.Mocker.

Test enviroments

  • pip freeze is identical for both virtualenvs:
certifi==2017.7.27.1
chardet==3.0.4
idna==2.6
py==1.4.34
pytest==3.2.1
requests==2.18.4
requests-mock==1.3.0
six==1.10.0
urllib3==1.22
  • pytest environments:
platform darwin -- Python 2.7.10, pytest-3.2.1, py-1.4.34, pluggy-0.4.0
rootdir: /tmp/foo, inifile:
platform darwin -- Python 3.6.2, pytest-3.2.1, py-1.4.34, pluggy-0.4.0
rootdir: /tmp/foo, inifile:
  • full error:
E       fixture 'mocked_obj' not found
>       available fixtures: cache, capfd, capsys, doctest_namespace, monkeypatch, pytestconfig, record_xml_property, recwarn, tmpdir, tmpdir_factory
>       use 'pytest --fixtures [testpath]' for help on them.
@RonnyPfannschmidt
Copy link
Member

based on http://bazaar.launchpad.net/~jamielennox/requests-mock/master/view/head:/requests_mock/mocker.py#L210 its not directly possible to figure this from pytest - requests-mocker itself kills it

the difference between py2 and py3 is that on python2 functools.wraps is simply completely broken

this also means that pytest cant even hope to fix it without requests-mocker providing metadata

i'm inclined to either close this or mark it as upstream being too broken to fix this sanely

@volans-
Copy link
Author

volans- commented Sep 3, 2017

@RonnyPfannschmidt thanks for your quick reply, I've opened https://bugs.launchpad.net/requests-mock/+bug/1714756 for upstream.

@RonnyPfannschmidt
Copy link
Member

thanks for the followup - it may be necessary to work a bit together to handle it similar to

https://github.com/pytest-dev/pytest/blob/master/_pytest/compat.py#L86

however as requests-mock is not part of the stdlib/as well used, it may work out better if it provided a pytest fixture instead

as far as i understood its internal api is very suitable for that kind of approach and integration with pytest would be more clean also

@jamielennox
Copy link

Hi, so i'll admit i'm not a big fan of the class decorator approach in general, but it was contributed to emulate an existing pattern so I see the benefit in having it there. I know some of the problems of wraps() but have never really used pytest to know what it is doing here. If anything I was under the impression this was better in python3, not worse.

So I can dive into this because I certainly would like requests-mock to work with pytest (and thought it did), but if can you short circuit that for me and tell me what metadata is required that would be faster.

On that though I completely agree with @RonnyPfannschmidt my preferred use of requests-mock is via the requests_mock/contrib/fixture.py fixture for the testing-cabal/fixtures library. I'd happily add a pytest equivalent of that to contrib/ to make it work in a pytest native way, I just don't know what that looks like.

@RonnyPfannschmidt
Copy link
Member

@jamielennox the "problem" here is that wraps is better on python3 - so py.test is able to see the actual function with actual parameters instead of the wrapper function only

since there is no declared signature mechanism to understand the arguments added by the decorators, its not handle-able

for mock as in the stdlib we hook into its own metadata which sets up a list of patchings so we can make a reasonable cut-off

@jamielennox
Copy link

Ok, understood - in python 3 you can see the mocked_obj parameter where it wasn't introspectable on python 2.

Just to understood i caught that right about the signature mechanism - you're saying that pytest has no way to mark that a parameter is just a parameter and not a reference to a fixture? I'd happily add it as like a function attribute or to a docstring or something that won't get in the way of any other suite.

In which case really the only option may be the pytest specific fixture as any of the current mockers that pass the mocker as a parameter are not going to work in pytest. Is there an existing example of how a third party would provide this fixture in a reasonable way. I can see the basics, but there's a whole lot of parameters and location hierarchy (newbie) that makes fixtures seem like something projects should define individually.

@RonnyPfannschmidt
Copy link
Member

the most easy way wouldbe for you to provide either a manually addable or a automatically loaded pytest plugin

all that would have to do is declare a pytest fixture

then it would be available either automatically or by requesting a requests_mock.integrations.pytest_plugin plugin in a conftest/pytest.ini

@nicoddemus nicoddemus added the status: needs information reporter needs to provide more information; can be closed after 2 or more weeks of inactivity label Sep 6, 2017
@andy-maier
Copy link
Contributor

I have the same problem as originally reported in this issue.

I have no problem switching from the decorator approach (shown in the example in the original post) to an explicit pytest.fixture in my test program, but I don't know how that would be done with requests_mock (the example in its - very short- chapter about fixtures seems to only work with testtools).

How can I use pytest with requests_mock today?

@jamielennox
Copy link

Hey, sorry for the delay, i've been on a beach :).

So the fixtures doc is specifically about the fixtures module: https://pypi.python.org/pypi/fixtures - this is unfortunately very generically named, but what i was working with a lot at the time.

The problem I'm having here is not can requests_mock and pytest integrate, but I don't know enough about pytest to know how to do this in a useful repeatable way that is worth including into the library.

Here's the simplest thing i can see to combine the two: https://gist.github.com/jamielennox/d996d818eb98c5f38a8e0eb3b7376bd7
but i will need some help from someone with pytest experience to know about scopes and where to put the module so it's correctly imported etc.

@mykwillis
Copy link

mykwillis commented Dec 9, 2017

@jamielennox I forked your gist to show how to take advantage of pytest's support for fixtures that yield, which solves the problem of proper teardown. This lets you use the existing requests_mock.Mocker context manager like so:

@pytest.fixture()
def m():
    with requests_mock.Mocker() as m:
        yield m

You can then use this fixture with other pytest fixtures in test cases like:

def test_foo(m, n, p):  # n, p are other fixtures
    m.post(...)

https://gist.github.com/mykwillis/3785c8f92e600abf9a33b46568206b10

@jamielennox
Copy link

that's pretty cool and definitely better looking, thanks.

I guess where we get to with this thread though is still: is there a way i can/should ship that snippet requests_mock or do i just document it for people?

The existing example of this is using fixtures you can pip install requests-mock[fixture] to include the dependencies (if you don't manage that externally)

and then

from requests_mock.contrib import fixture
import testtools

class MyTestCase(testtools.TestCase):
    def setUp(self):
        super(MyTestCase, self).setUp()
        self.requests_mock = self.useFixture(fixture.Fixture())

Having not used pytest, i read through docs and can't tell if it's even possible to have fixtures in external modules, or what the scopes should be.

But that yield based snippet is so simple maybe the best thing is to just include it in the documentation?

@nicoddemus
Copy link
Member

nicoddemus commented Dec 26, 2017

Perhaps requests-mock can provide a that requests_mock fixture directly for use with pytest?

it's even possible to have fixtures in external modules, or what the scopes should be.

Definitely, that's what plugins do. 😁

Basically you need to define that requests_mock fixture in a module, say in requests_mock.pytest_plugin, and then add this to your setup.py:

setup(
    ...
    # the following makes a plugin available to pytest
    entry_points = {
        'pytest11': [
            'requests_mock = requests_mock.pytest_plugin',
        ]
    },
)

And that's it. pytest users installing requests_mock now have access to that fixture. Perhaps you should name the fixture a different name than the module, otherwise it might confuse users. I had the same issue when the pytest-mock plugin had a fixture named mock initially: users would confuse the fixture with the mock module. Before 1.0 I ended up renaming it to mocker instead.

See more info at writing plugins.

@BenjamenMeyer
Copy link

Is it possible add a note about this kind of issue in an FAQ?

Not every project will be able to convert to fixtures, so some guidance would be great.

In my case, I'm converting StackInABox (https://pypi.python.org/pypi/stackinabox) from using nosetests for itself to using pytest and want to continue to support the functionality AS-IS so I can show the flexibility. I'm planning on also adding fixture support but need to be able to show both with and without fixtures.

The work-around works for me, but it'd be great to give it some visibility for those in my situation - especially once this issue is closed out. $0.02

@RonnyPfannschmidt
Copy link
Member

with recent pytest you should be able to use signature objects via funcsigs/funcsigs2

@nicoddemus
Copy link
Member

Meanwhile @jamielennox, any news on making that fixture available?

@jamielennox
Copy link

If someone can test jamielennox/requests-mock#51 for me i'll merge it soon and do a new release.

I'll try and add some local tests tomorrow, but they won't really merge with the existing ones.

@nicoddemus
Copy link
Member

I tested in jamielennox/requests-mock#51 (comment) and it is working fine, nice job! 👍

gerrit-ovirt-org pushed a commit to oVirt/ovirt-provider-ovn that referenced this issue Feb 14, 2019
The fixture 'requests_mock' is not available on py3, as per [0].

'requests_mock' fixture, on the other hand is, and is injected
into the pytest framework through a plugin.

[0] - pytest-dev/pytest#2749

Bug-Url: https://bugzilla.redhat.com/1641353
Change-Id: I53311c8a569edf7be201ba512c199ab8ef018d61
Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: needs information reporter needs to provide more information; can be closed after 2 or more weeks of inactivity
Projects
None yet
Development

No branches or pull requests

7 participants