Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Confusion around assert_all_mocked behavior #204

Closed
sihrc opened this issue May 8, 2022 · 2 comments
Closed

Confusion around assert_all_mocked behavior #204

sihrc opened this issue May 8, 2022 · 2 comments

Comments

@sihrc
Copy link

sihrc commented May 8, 2022

@respx.mock(assert_all_mocked=True)
async def test_httpx_all_mocked_true():

    async with httpx.AsyncClient() as client:
        request = respx.get("http://test.com/id").mock(
            return_value=httpx.Response(200, content="test")
        )
        resp = await client.get("http://test.com/id")
        assert resp.content == b"test"


@respx.mock(assert_all_mocked=False)
async def test_httpx_all_mocked_false():

    async with httpx.AsyncClient() as client:
        request = respx.get("http://test.com/id").mock(
            return_value=httpx.Response(200, content="test")
        )
        resp = await client.get("http://test.com/id")
        assert resp.content == b"test"


@respx.mock
async def test_httpx_all_mocked_none():

    async with httpx.AsyncClient() as client:
        request = respx.get("http://test.com/id").mock(
            return_value=httpx.Response(200, content="test")
        )
        resp = await client.get("http://test.com/id")
        assert resp.content == b"test"

Given these 3 identical tests with different assert_all_mocked inputs, here is the output:

================================== FAILURES ==================================
_________________________ test_httpx_all_mocked_true _________________________

    @respx.mock(assert_all_mocked=True)
    async def test_httpx_all_mocked_true():

        async with httpx.AsyncClient() as client:
            request = respx.get("http://test.com/id").mock(
                return_value=httpx.Response(200, content="test")
            )
>           resp = await client.get("http://test.com/id")

tests/routes/test_twitch_users_routes.py:19:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/usr/local/lib/python3.10/site-packages/httpx/_client.py:1729: in get
    return await self.request(
/usr/local/lib/python3.10/site-packages/httpx/_client.py:1506: in request
    return await self.send(request, auth=auth, follow_redirects=follow_redirects)
/usr/local/lib/python3.10/site-packages/httpx/_client.py:1593: in send
    response = await self._send_handling_auth(
/usr/local/lib/python3.10/site-packages/httpx/_client.py:1621: in _send_handling_auth
    response = await self._send_handling_redirects(
/usr/local/lib/python3.10/site-packages/httpx/_client.py:1658: in _send_handling_redirects
    response = await self._send_single_request(request)
/usr/local/lib/python3.10/site-packages/httpx/_client.py:1695: in _send_single_request
    response = await transport.handle_async_request(request)
/usr/local/lib/python3.10/site-packages/httpx/_transports/default.py:353: in handle_async_request
    resp = await self._pool.handle_async_request(req)
/usr/local/lib/python3.10/site-packages/respx/mocks.py:188: in amock
    response = await cls._send_async_request(
/usr/local/lib/python3.10/site-packages/respx/mocks.py:222: in _send_async_request
    httpx_response = await cls.async_handler(httpx_request)
/usr/local/lib/python3.10/site-packages/respx/mocks.py:134: in async_handler
    raise assertion_error
/usr/local/lib/python3.10/site-packages/respx/mocks.py:127: in async_handler
    httpx_response = await router.async_handler(httpx_request)
/usr/local/lib/python3.10/site-packages/respx/router.py:319: in async_handler
    resolved = await self.aresolve(request)
/usr/local/lib/python3.10/site-packages/respx/router.py:292: in aresolve
    with self.resolver(request) as resolved:
/usr/local/lib/python3.10/contextlib.py:142: in __exit__
    next(self.gen)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <respx.router.MockRouter object at 0x7f87b1b2b9a0>
request = <Request('GET', 'http://test.com/id')>

    @contextmanager
    def resolver(self, request: httpx.Request) -> Generator[ResolvedRoute, None, None]:
        resolved = ResolvedRoute()

        try:
            yield resolved

            if resolved.route is None:
                # Assert we always get a route match, if check is enabled
                if self._assert_all_mocked:
>                   raise AllMockedAssertionError(f"RESPX: {request!r} not mocked!")
E                   respx.models.AllMockedAssertionError: RESPX: <Request('GET', 'http://test.com/id')> not mocked!

/usr/local/lib/python3.10/site-packages/respx/router.py:251: AllMockedAssertionError
________________________ test_httpx_all_mocked_false _________________________

    @respx.mock(assert_all_mocked=False)
    async def test_httpx_all_mocked_false():

        async with httpx.AsyncClient() as client:
            request = respx.get("http://test.com/id").mock(
                return_value=httpx.Response(200, content="test")
            )
            resp = await client.get("http://test.com/id")
>           assert resp.content == b"test"
E           AssertionError: assert b'' == b'test'
E             Full diff:
E             - b'test'
E             + b''

tests/routes/test_twitch_users_routes.py:31: AssertionError
========================== short test summary info ===========================
FAILED tests/routes/test_twitch_users_routes.py::test_httpx_all_mocked_true
FAILED tests/routes/test_twitch_users_routes.py::test_httpx_all_mocked_false
======================== 2 failed, 1 passed in 0.53s =========================

Shouldn't all of these tests pass if the tests are identical and rely on the mock?
Also, I see that the default assert_all_mocked value is True. Why is it that the behavior of not passing in a value is different from passing in True?

@sihrc
Copy link
Author

sihrc commented May 8, 2022

Furthermore, it appears passing in anything to the respx.mock decorator changes behavior:

e.g.

@respx.mock(base_url="http://example.com")
async def test_with_base_url():
    async with httpx.AsyncClient() as client:
        respx.get("/user").mock(return_value=httpx.Response(200, content="test"))
        resp = await client.get("http://example.com/user")
        assert resp.content == b"test"


@respx.mock
async def test_without_base_url():
    async with httpx.AsyncClient() as client:
        respx.get("http://example.com/user").mock(
            return_value=httpx.Response(200, content="test")
        )
        resp = await client.get("http://example.com/user")
        assert resp.content == b"test"

Results in:

================================== FAILURES ==================================
_____________________________ test_with_base_url _____________________________

    @respx.mock(base_url="http://example.com")
    async def test_with_base_url():
        async with httpx.AsyncClient() as client:
            respx.get("/user").mock(return_value=httpx.Response(200, content="test"))
>           resp = await client.get("http://example.com/user")

tests/routes/test_twitch_users_routes.py:9:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/usr/local/lib/python3.10/site-packages/httpx/_client.py:1729: in get
    return await self.request(
/usr/local/lib/python3.10/site-packages/httpx/_client.py:1506: in request
    return await self.send(request, auth=auth, follow_redirects=follow_redirects)
/usr/local/lib/python3.10/site-packages/httpx/_client.py:1593: in send
    response = await self._send_handling_auth(
/usr/local/lib/python3.10/site-packages/httpx/_client.py:1621: in _send_handling_auth
    response = await self._send_handling_redirects(
/usr/local/lib/python3.10/site-packages/httpx/_client.py:1658: in _send_handling_redirects
    response = await self._send_single_request(request)
/usr/local/lib/python3.10/site-packages/httpx/_client.py:1695: in _send_single_request
    response = await transport.handle_async_request(request)
/usr/local/lib/python3.10/site-packages/httpx/_transports/default.py:353: in handle_async_request
    resp = await self._pool.handle_async_request(req)
/usr/local/lib/python3.10/site-packages/respx/mocks.py:188: in amock
    response = await cls._send_async_request(
/usr/local/lib/python3.10/site-packages/respx/mocks.py:222: in _send_async_request
    httpx_response = await cls.async_handler(httpx_request)
/usr/local/lib/python3.10/site-packages/respx/mocks.py:134: in async_handler
    raise assertion_error
/usr/local/lib/python3.10/site-packages/respx/mocks.py:127: in async_handler
    httpx_response = await router.async_handler(httpx_request)
/usr/local/lib/python3.10/site-packages/respx/router.py:319: in async_handler
    resolved = await self.aresolve(request)
/usr/local/lib/python3.10/site-packages/respx/router.py:292: in aresolve
    with self.resolver(request) as resolved:
/usr/local/lib/python3.10/contextlib.py:142: in __exit__
    next(self.gen)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <respx.router.MockRouter object at 0x7fb4d9417d30>
request = <Request('GET', 'http://example.com/user')>

    @contextmanager
    def resolver(self, request: httpx.Request) -> Generator[ResolvedRoute, None, None]:
        resolved = ResolvedRoute()

        try:
            yield resolved

            if resolved.route is None:
                # Assert we always get a route match, if check is enabled
                if self._assert_all_mocked:
>                   raise AllMockedAssertionError(f"RESPX: {request!r} not mocked!")
E                   respx.models.AllMockedAssertionError: RESPX: <Request('GET', 'http://example.com/user')> not mocked!

/usr/local/lib/python3.10/site-packages/respx/router.py:251: AllMockedAssertionError
========================== short test summary info ===========================
FAILED tests/routes/test_twitch_users_routes.py::test_with_base_url - respx...
======================== 1 failed, 1 passed in 0.44s =========================```

@lundberg
Copy link
Owner

lundberg commented May 9, 2022

Furthermore, it appears passing in anything to the respx.mock decorator changes behavior

Yes, that's correct 😉. Short answer; passing args to respx.mock yields a respx_mock router/arg to your test function.

This is explained here in the docs, also answered in #196 and #197.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants