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

How to mock and test request with content passed as byte iterator #148

Closed
adamko147 opened this issue Aug 19, 2024 · 4 comments
Closed

How to mock and test request with content passed as byte iterator #148

adamko147 opened this issue Aug 19, 2024 · 4 comments
Labels
bug Something isn't working

Comments

@adamko147
Copy link

adamko147 commented Aug 19, 2024

Hello team,

I have httpx code that passes content to request using byte iterator. When trying the test code using pytest_httpx and match_content on mocked request, I get assertion error and the aiter_bytes is never called. Here's what I'm trying...

import httpx
import pytest


async def aiter_bytes():
    yield b"h"
    yield b"e"
    yield b"l"
    yield b"l"
    yield b"o"


@pytest.mark.asyncio
async def test_httpx_get(httpx_mock):
    httpx_mock.add_response(method="PUT", url="http://put", content=b"world", match_content=b"hello")
    async with httpx.AsyncClient() as client:
        res = await client.put("http://put", content=aiter_bytes())
        assert res.text == "world"

and the exception when runnning the test

_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Request('PUT', 'http://put')>

    def read(self) -> bytes:
        """
        Read and return the request content.
        """
        if not hasattr(self, "_content"):
>           assert isinstance(self.stream, typing.Iterable)
E           AssertionError

When I remove the match_content parameter, the test passes, but the aiter_bytes is never called. Is there anything I'm missing? I'm not sure whether this is pytest_httpx or httpx issue, although when I run the code without pytest in real program, it works and sends the data properly.

Thank you,
Adam

@adamko147 adamko147 changed the title How to test request content with content provided as byte iterator How to mock and test request with content passed as byte iterator Aug 19, 2024
@Colin-b
Copy link
Owner

Colin-b commented Sep 20, 2024

Hello,

The documentation is already explaining how to stream chunks

If something is not working in the provided example, feel free to report it.

Thanks

@Colin-b Colin-b closed this as completed Sep 20, 2024
@Colin-b Colin-b added the question Further information is requested label Sep 20, 2024
@adamko147
Copy link
Author

adamko147 commented Sep 20, 2024

Hello,

The documentation is already explaining how to stream chunks

If something is not working in the provided example, feel free to report it.

Thanks

Hi @Colin-b, thanks for reaching out. I'am aware of the documentation, although the docs covers the case when mocked url produces the stream, which is not what I'm trying to achieve.

Following code passes fine

async def test_httpx_put(httpx_mock):
    httpx_mock.add_response(method="PUT", url="http://put", content=b"world", match_content=b"hello")
    async with httpx.AsyncClient() as client:
        res = await client.put("http://put", content=b"hello")
        assert res.text == "world"

and checks that I call http://put with "hello" content.

when I change the way of sending the data to async iterator as follows

async def aiter_bytes():
    yield b"h"
    yield b"e"
    yield b"l"
    yield b"l"
    yield b"o"


@pytest.mark.asyncio
async def test_httpx_put_iter(httpx_mock):
    httpx_mock.add_response(method="PUT", url="http://put", content=b"world", match_content=b"hello")
    async with httpx.AsyncClient() as client:
        res = await client.put("http://put", content=aiter_bytes())
        assert res.text == "world"

the execution fails with error

self = <Request('PUT', 'http://put')>

    def read(self) -> bytes:
        """
        Read and return the request content.
        """
        if not hasattr(self, "_content"):
>           assert isinstance(self.stream, typing.Iterable)
E           AssertionError

comming from pytest_httpx/_request_matcher.py:101: in _content_match

The code should work, based on httpx documentation you can pass content as Binary content to include in the body of the request, as bytes or a byte iterator.

The content is typing.AsyncIterable in this case and therefore _RequestMatcher._content_match should use

if self.content is not None:
    return await request.aread() == self.content

to compare the content.

Should I open new issue or we can re-open this one?

Thank you!

@Colin-b Colin-b reopened this Sep 21, 2024
@Colin-b
Copy link
Owner

Colin-b commented Sep 21, 2024

My bad, I read this too fast. Indeed this looks like a bug !

Thanks for reporting, I'll try to find some time

@Colin-b Colin-b added bug Something isn't working and removed question Further information is requested labels Sep 21, 2024
@Colin-b Colin-b mentioned this issue Sep 22, 2024
@Colin-b
Copy link
Owner

Colin-b commented Sep 22, 2024

Fixed in version 0.31.1 available on pypi

@Colin-b Colin-b closed this as completed Sep 22, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants