pytest_httpx icon indicating copy to clipboard operation
pytest_httpx copied to clipboard

0.33's `should_mock` break our tests

Open mvanderlee opened this issue 5 months ago • 5 comments

We're still on 0.30 because 0.31 broke test for us. I'm trying to upgrade to 0.35 for the is_optional feature, but we only want to mock those requests that actually have mocked responses. and even though the below code works around the 0.31 issues. Now should_mock breaks everything. And I see no way to apply a global 'only mock if any matcher matches' criteria.

Ideally there is a single option for that. Or at minimum, should_mock should be called with all matchers.

def pytest_collection_modifyitems(session, config, items):
    for item in items:
        item.add_marker(
            pytest.mark.httpx_mock(
                assert_all_requests_were_expected=False,
                assert_all_responses_were_requested=False,
            ),
        )

mvanderlee avatar Jun 18 '25 13:06 mvanderlee

Hello @mvanderlee

Can you share what you were doing in v0.30? I have trouble understanding your use case.

Thanks again

Colin-b avatar Jun 18 '25 14:06 Colin-b

For our test suite, only requests that are explicitly mocked should be mocked. All others should actually call the third party service.

While technically we could add a custom should_mock to every single test.. as you can imagine that would be incredibly verbose and time consuming. We now have a few scenarios where based on certain criteria we want to call URL A, or URL B. We'd like to be able to use parametrize for this and fixtures for re-use.

Essentially this test

i.e.:

@pytest.fixture
def mock_service_a(httpx_mock):
    httpx_mock.add_response(url="https://service_a", content="hello world")

@pytest.fixture
def mock_service_b(httpx_mock):
    httpx_mock.add_response(url="https://service_b", content="goodbye world")

@pytest.mark.parametrize("use_service_a", [True, False])
def test_service_call(
   test_client,
    mock_service_a,
    mock_service_b,
    use_service_a,
    httpx_mock,
):
    test_client.post("/my_api", json={"use_service_a": use_service_a})
    request_a = httpx_mock.get_request(url="https://service_a")
    request_a = httpx_mock.get_request(url="https://service_b")
    
    if use_service_a:
        assert request_a is not None
        assert request_b is None
    else:
        assert request_a is None
        assert request_b is not None

mvanderlee avatar Jun 18 '25 14:06 mvanderlee

Side note, possible needs to be a different ticket. But the fact that it raises a Timeout exception if no matcher is found made it very time consuming to even realize this was happening in the first place. Because there were a number of timeout handlers in between my test and the actual http call. So I never saw this library's error message. Having a clear raise RequestNotMockedError or something would've made life a lot easier.

mvanderlee avatar Jun 18 '25 15:06 mvanderlee

I see several points here:

  1. You want to be able to add optional responses (might or might not be called depending on context). This is indeed possible via the is_optional parameter.
  2. You want to mock only when a response is registered, and otherwise the request should go through. This is indeed expected to be managed via the should_mock option. However the callable only provide the request for now. Even if we were to expose the httpx_mock fixture, we would need to expose something so that you could check if the current request is mocked. In the meantime, and this is not a clean solution, but as a workaround it can work depending on your setup, you can have a wrapper around your responses registration saving the mocked URLs in a global variable that you would check in should_mock.
  3. You want a different behavior upon unmatched requests, this is a new feature that could indeed be a nice addition. It could even handle your specific case in 2) as we could expose the ability to provide a callable taking the real transport and the request, so that you could send the request to the real transport in such a case.

Colin-b avatar Jun 18 '25 18:06 Colin-b

Yes, good summary.

mvanderlee avatar Jun 18 '25 19:06 mvanderlee

Closing this, we've updated to 0.35 and changed our tests to fit the project.

mvanderlee avatar Sep 12 '25 21:09 mvanderlee