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.
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.
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
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:
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.
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
@RonnyPfannschmidt thanks for your quick reply, I've opened https://bugs.launchpad.net/requests-mock/+bug/1714756 for upstream.
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
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.
@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
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.
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
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?
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.
@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
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?
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.
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
with recent pytest you should be able to use signature objects via funcsigs/funcsigs2
Meanwhile @jamielennox, any news on making that fixture available?
If someone can test https://github.com/jamielennox/requests-mock/pull/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.
I tested in https://github.com/jamielennox/requests-mock/pull/51#issuecomment-385704346 and it is working fine, nice job! 馃憤
Most helpful comment
@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:
You can then use this fixture with other pytest fixtures in test cases like:
https://gist.github.com/mykwillis/3785c8f92e600abf9a33b46568206b10