From 815a16acd222f03578b6d73cc067f4026a0fb25f Mon Sep 17 00:00:00 2001 From: Felix Scherz Date: Tue, 14 May 2024 21:31:05 +0200 Subject: [PATCH] feat: control request assertion on callback level --- pytest_httpx/_httpx_mock.py | 27 ++++++++++++++++++++------- tests/test_plugin.py | 20 ++++++++++++++++++++ 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/pytest_httpx/_httpx_mock.py b/pytest_httpx/_httpx_mock.py index 941b050..2a06546 100644 --- a/pytest_httpx/_httpx_mock.py +++ b/pytest_httpx/_httpx_mock.py @@ -23,6 +23,7 @@ def __init__(self) -> None: Optional[httpx.Response], Awaitable[Optional[httpx.Response]] ], ], + bool, ] ] = [] @@ -36,6 +37,7 @@ def add_response( html: Optional[str] = None, stream: Any = None, json: Any = None, + assert_requested=True, **matchers: Any, ) -> None: """ @@ -73,7 +75,9 @@ def response_callback(request: httpx.Request) -> httpx.Response: stream=stream, ) - self.add_callback(response_callback, **matchers) + self.add_callback( + response_callback, assert_requested=assert_requested, **matchers + ) def add_callback( self, @@ -81,6 +85,7 @@ def add_callback( [httpx.Request], Union[Optional[httpx.Response], Awaitable[Optional[httpx.Response]]], ], + assert_requested=True, **matchers: Any, ) -> None: """ @@ -97,9 +102,13 @@ def add_callback( :param match_content: Full HTTP body identifying the request(s) to match. Must be bytes. :param match_json: JSON decoded HTTP body identifying the request(s) to match. Must be JSON encodable. """ - self._callbacks.append((_RequestMatcher(**matchers), callback)) + self._callbacks.append( + (_RequestMatcher(**matchers), callback, assert_requested) + ) - def add_exception(self, exception: Exception, **matchers: Any) -> None: + def add_exception( + self, exception: Exception, assert_requested=True, **matchers: Any + ) -> None: """ Raise an exception if a request match. @@ -119,7 +128,9 @@ def exception_callback(request: httpx.Request) -> None: exception.request = request raise exception - self.add_callback(exception_callback, **matchers) + self.add_callback( + exception_callback, assert_requested=assert_requested, **matchers + ) def _handle_request( self, @@ -166,7 +177,7 @@ def _explain_that_no_response_was_found( real_transport: Union[httpx.BaseTransport, httpx.AsyncBaseTransport], request: httpx.Request, ) -> str: - matchers = [matcher for matcher, _ in self._callbacks] + matchers = [matcher for matcher, _, _ in self._callbacks] message = f"No response can be found for {RequestDescription(real_transport, request, matchers)}" @@ -188,7 +199,7 @@ def _get_callback( ]: callbacks = [ (matcher, callback) - for matcher, callback in self._callbacks + for matcher, callback, _ in self._callbacks if matcher.match(real_transport, request) ] @@ -260,7 +271,9 @@ def reset(self, assert_all_responses_were_requested: bool) -> None: def _reset_callbacks(self) -> list[_RequestMatcher]: callbacks_not_executed = [ - matcher for matcher, _ in self._callbacks if not matcher.nb_calls + matcher + for matcher, _, assert_requested in self._callbacks + if not matcher.nb_calls and assert_requested ] self._callbacks.clear() return callbacks_not_executed diff --git a/tests/test_plugin.py b/tests/test_plugin.py index f821beb..cd38425 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -109,6 +109,26 @@ def unused(*args, **kwargs): result.assert_outcomes(passed=1) +def test_httpx_mock_unused_single_unused_callback(testdir: Testdir) -> None: + """ + Unused callbacks should not fail test case if assert_all_responses_were_requested fixture is set to False. + """ + testdir.makepyfile( + """ + import pytest + + def test_httpx_mock_unused_callback_without_assertion(httpx_mock): + def unused(*args, **kwargs): + pass + + httpx_mock.add_callback(unused, assert_requested=False) + + """ + ) + result = testdir.runpytest() + result.assert_outcomes(passed=1) + + def test_httpx_mock_non_mocked_hosts_sync(testdir: Testdir) -> None: """ Non mocked hosts should go through while other requests should be mocked.