outer mock broken by nested mocks
Affects | Status | Importance | Assigned to | Milestone | |
---|---|---|---|---|---|
requests-mock |
New
|
Undecided
|
Unassigned |
Bug Description
I was using 0.6 and nesting didn't work: the inner mock didn't take effect.
I was thrilled when I saw "Allow for nested mocking".
However, now the outer mock doesn't take effect. This is still broken from my perspective.
----
In [8]: with requests_
...: m.get('http://
...: with requests_
...: m2.get('http://
...: r = requests.get('http://
...: print(r.content)
...:
<snip>
NoMockAddress: No mock address: GET http://
---
I actually prefer the old behavior (and am suggesting my team revert to the older version), because at least then it was clear that nested mocks don't work. Now it's easy to get tripped up by the mistake I just made: We have a base class that sets up a bunch of mocks. I was writing a test that needed a new mock. Initially my test didn't happen to use the mocks from the base class, so I didn't know that anything was wrong. But then things changed slightly and the base class's mocks were required, and it all broke.
I think the ideal behavior would be to try the inner mock first, and if there's no match go to the outer mock.
Hey David, Thanks for the report.
So i'm sorry to have flipped behaviour like that, interestingly the reason I did was because someone filed a bug report saying the old way was unintuitive. At the time I did consider chaining up from inner to outer mocks, but there are a few implementation details that make that difficult and that was the first time i'd even heard of somebody doing nested mocks.
So before i commit to anything i'd really like to understand the use case because I've never seen why people use requests-mock this way. You say "I was writing a test that needed a new mock", what's the rationale for using a new mock instead of adding a new route to the old mock?
The way i've always done my tests is (untested):
class TestStuff( testtools. TestCase) :
def setUp(self):
super( TestStuff, self).setUp()
self.requests_ mock = self.useFixture (rm_fixture. Fixture( ))
self.requests_ mock.get( 'https:/ /test.com/ login', json={})
// some common routes, eg auth
def test_something( self):
self.requests_ mock.get( 'https:/ /test.com/ path', json={})
// test specific mocks
// do test
(I like the fixture because we use fixtures elsewhere, but all it does is creates a requests_ mock.Mocker( ), call rm.start() and addCleanup(rm.stop) so you don't need to use it)
This means that you have one mocker object per test and your test suite builds up relevant mocks in the setUp, and typically we have a test class inheritence that means you get all the common mocks based on what you are inheriting.
I'm not claiming my way is correct, but approaching tests that way I'm not sure why you're in a situation where you have a new mocker object for a test and that test also relies on a mocker that was set up earlier. Why not just reuse the first one?