lundberg/respx

assert_all_mocked - default auto mocked

Lujeni opened this issue · 11 comments

Hello,

Thanks for your great tool ! its very useful.
I am not sure to well understand the option assert_all_mocked and why the default behavior when disabled is all non-routed requests will be auto mocked with status code 200.

In my case, I trigger many httpx calls on many API.
I just wanna mock one specific call (e.g. side effect) and let the rest of calls

Thanks

So, the assert_all_mocked assertion check is a "safety" feature to make sure all requests, sent by any of your HTTPX clients, are mocked by RESPX, i.e. assert that all sent requests are mocked by you with a desired and testable response or side effect.

See it as your own proof that all endpoints that your application will call is specified in RESPX with proper mocked responses to not get any false positive test results 😉 .

By disabling this check, RESPX will not raise this assertion error for non-matched requests (e.g. matching url etc), and will instead auto mock a 200 response, to prevent the request to call remotely.

If you want a specific request to actually call remotely, and not auto mock with 200, the route needs to be marked to pass through.

I just wanna mock one specific call (e.g. side effect) and let the rest of calls

This sentence seems cut off, could you explain in more detail what you want to achieve?

@lundberg thanks for your answer

To give you a concrete example;

@pytest.fixture
def mock_api_two_post(respx_mock):
    respx_mock.post(....)

def test_create_object(mock_api_two_post):
    ....

Your example looks OK, i.e. you only need to mock https://api-two.com/object/ POST in your fixture if you're only testing that endpoint in the test case. The other endpoints will be auto mocked with a 200 if assert_all_mocked is disabled.

Though, it would probably make more sense if you mock all endpoints in your fixture and named it mock_api, instead of creating a fixture per endpoint.

If you want to change the response for a specific endpoint in a specific test case, then you can access the route by name and change the return_value or side effect within the test case...like this

[...] The other endpoints will be auto mocked with a 200 if assert_all_mocked is disabled.

Its my point, I dont want an auto mocked, I want the "real" API response.
From what I understand, I should use an Pass Through for the first 3 calls and then mock the latest call.

From what I understand, I should use an Pass Through for the first 3 calls and then mock the latest call.

Yep, thats correct, if you want those other three calls to hit remote network endpoints, you need to add them as well and mark each as .pass_through(). Not that common to call externally in a test case, but pass the through feature is there for those cases 😉

FYI, the added "routes" in RESPX are matched in added order, meaning you can add your mocked response first and then add a "catch-all" pass through route after the first one that would handle all three endpoints.

Something like...

respx_mock.post("https://api-two.com/object/").respond(...)
respx_mock.route(host="https://api-two.com").pass_through()

I guess your problem is solved and we can consider closing this issue @Lujeni?

@lundberg yep I understand better . thanks for your time and you answers !

I'm hitting this as well (coucou @Lujeni ❤️).

I'm writing E2E tests and I only want to mock one single endpoint (the authentication one). I want all other HTTP calls to pass through, without having to specify them all one by one, because there are a lot and it's very likely that they change.

What would you think of adding a mock_by_default: bool option or something in those lines, @lundberg?

@ramnes your case is very similar and I'd suggest the solution above for now.

To give it some more light .. mocked routes can be how wide or narrow as needed, e.g. exact url + params + headers, or as wide as just a host for example.

In your case, I'd say first add a route for the authentication endpoint and then a second catch all route that is marked for pass through, making any other request hit remote.

Something like ..

# your mocked auth
respx.post(auth_url).mock(...)

# a route without any patterns that'll match any request ..
respx.route().pass_through()
# .. or make it a bit narrower
respx.route(host=e2e_host).pass_through()

Hope that helps.

What would you think of adding a mock_by_default: bool option or something in those lines, @lundberg?

Not sure that it's needed, but of course wouldn't hurt to have the option.

Maybe an option to define a custom default response instead of the current empty 200?

Actually I didn't know that an empty route was an option; that's great, no need for more. Maybe that would be worth an example in the pass_through docs?

Great @ramnes .

I opened two issues regarding your issue and comments.