vcrpy icon indicating copy to clipboard operation
vcrpy copied to clipboard

fix Starlette TestClient

Open alekseik1 opened this issue 1 year ago • 2 comments

Hi, I'm trying to get Starlette TestClient running with vcrpy. The problem is that assert not hasattr(resp, "_decoder") fails in _to_serialized_response func, which breaks vcr for Starlette TestClient, see #628.

I see two possible solutions here:

First approach (implemented)

From docs/advanced.rst:

Requests that are ignored by VCR will not be saved in a cassette, nor played back from a cassette. VCR will completely ignore those requests as if it didn't notice them at all, and they will continue to hit the server as if VCR were not there.

Which means it is safe to completely ignore these requests when recording responses. If we don't record responses, we don't hit this assert.

Second approach

Fix buggy _run_async_function (sorry, it was me who messed it up):

def _run_async_function(sync_func, *args, **kwargs):
    """
    Safely run an asynchronous function from a synchronous context.
    Handles both cases:
    - An event loop is already running.
    - No event loop exists yet.
    """
    try:
        loop = asyncio.get_running_loop()
    except RuntimeError:
        return asyncio.run(sync_func(*args, **kwargs))
    else:
        # If inside a running loop, create a task and wait for it
        return loop.run_in_executor(None, partial(sync_func, *args, **kwargs))  # <<< see here

Because right now it is bugged - we run a task in asyncio but never wait for it to finish.

@jairhenrique what do you think? Should I apply both fixes?

Fixes #628.

alekseik1 avatar May 04 '25 15:05 alekseik1

you know what. I think these asyncio-hacks are bad things to support. It's better to duplicate code than to manage event loops.

alekseik1 avatar May 05 '25 09:05 alekseik1

any updates on this? @kevin1024 @jairhenrique

alekseik1 avatar May 08 '25 10:05 alekseik1

@alekseik1 I wonder what is your setup for test. Do you use TestClient from starlette or fastapi as sync or async? @Kludex recommends using https.AsyncClient instead of TestClient if you want your test functions to be async https://github.com/Kludex/fastapi-tips?tab=readme-ov-file#5-use-httpxs-asyncclient-instead-of-testclient Also I had similar issue when I had following situation


@pytest.fixture(scope="session")
def client():
    with TestClient(app) as test_client:
        yield test_client


async test(client):
    response = client.get("/")
    ...

where test wasn't really using asynchronous features and changing async def to def fixed it for me. Try that.

It also helped me to do the following:

def before_record_callback(request):
    if request.host == "testserver":
        return None
    return request

@pytest.fixture(scope="session")
def vcr_config():
    return {
        "before_record_request": before_record_callback
    }

which results in VCR not recording the testserver at all.

nhtgl avatar Nov 19 '25 09:11 nhtgl

How can I reproduce the issue this PR tries to solve?

Kludex avatar Nov 19 '25 09:11 Kludex