Skip to content

Commit

Permalink
Fix nested decorators causing mocks leaking (#500)
Browse files Browse the repository at this point in the history
* clean patcher on exit
* add tests and comments

Closes #481
  • Loading branch information
beliaev-maksim committed Feb 22, 2022
1 parent 6530139 commit a35896e
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 1 deletion.
2 changes: 2 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

* Added a registry that provides more strict ordering based on the invocation index.
See `responses.registries.OrderedRegistry`.
* Prevent `responses.activate` decorator to leak, if wrapped function called from within another
wrapped function. Also, allow calling of above mentioned chain. See #481 for more details.
* Expose `get_registry()` method of `RequestsMock` object. Replaces internal `_get_registry()`.
* `query_param_matcher` can now accept dictionaries with `int` and `float` values.
* Add support for the `loose` version of `query_param_matcher` via named argument `strict_match`.
Expand Down
10 changes: 9 additions & 1 deletion responses/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,7 @@ def reset(self):
self._registry = FirstMatchRegistry()
self._calls.reset()
self.passthru_prefixes = ()
self._patcher = None

def add(
self,
Expand Down Expand Up @@ -806,14 +807,21 @@ def _on_request(self, adapter, request, **kwargs):
return response

def start(self):
if self._patcher:
# we must not override value of the _patcher if already applied
return

def unbound_on_send(adapter, request, *a, **kwargs):
return self._on_request(adapter, request, *a, **kwargs)

self._patcher = std_mock.patch(target=self.target, new=unbound_on_send)
self._patcher.start()

def stop(self, allow_assert=True):
self._patcher.stop()
if self._patcher:
# prevent stopping unstarted patchers
self._patcher.stop()

if not self.assert_all_requests_are_fired:
return

Expand Down
35 changes: 35 additions & 0 deletions responses/test_responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -1983,3 +1983,38 @@ async def run():

await run()
assert_reset()


class TestMultipleWrappers:
"""Test to validate that multiple decorators could be applied.
Ensures that we can call one function that is wrapped with
``responses.activate`` decorator from within another wrapped function.
Validates that mock patch is not leaked to other tests.
For more detail refer to https://github.com/getsentry/responses/issues/481
"""

@responses.activate
def test_wrapped(self):
responses.add(responses.GET, "http://example.com/1", body="Hello 1")
assert b"Hello 1" == requests.get("http://example.com/1").content

@responses.activate
def test_call_another_wrapped_function(self):
self.test_wrapped()

def test_mock_not_leaked(self, httpserver):
"""
Validate that ``responses.activate`` does not leak to unpatched test.
Parameters
----------
httpserver : ContentServer
Mock real HTTP server
"""
httpserver.serve_content("OK", code=969, headers={"Content-Type": "text/plain"})

response = requests.get(httpserver.url)
assert response.status_code == 969

0 comments on commit a35896e

Please sign in to comment.