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

Automatic snapshot of Requests #159

Open
sidharthv96 opened this issue Aug 18, 2021 · 11 comments
Open

Automatic snapshot of Requests #159

sidharthv96 opened this issue Aug 18, 2021 · 11 comments

Comments

@sidharthv96
Copy link

Hi, I have a use-case where I'm querying over 80 different endpoints.
Currently the outputs are verified using pytest-snapshot.
But the tests won't run fast as the HTTP calls have to be made.

What I'm trying to achieve is a system where if I add a new module and run the test, it's HTTP calls will be run once and written to a file. For future runs, that response will be provided by the mock, so we can skip the HTTP calls.

Is such an approach feasible with enough modifications in respx, or are there any architectural limitations which would stop me from adding such a feature?

@lundberg
Copy link
Owner

lundberg commented Aug 19, 2021

This is a nice idea and feature for RESPX.

I think it would be quite simple to support without too much effort. We already have the side effects to play with.

I'm thinking a side effect that first looks for an existing file and returns a response if found, otherwise returns the request in the side effect to make it pass through to remote server. Only thing we're missing is a response hook to persist the real remote response once returned.

One way could be to enhance the pass through behaviour for side effects, e.g. keep allowing the request to be returned, but also allow returning a PassThrough instance with a response callback hook.

Rough example...

def persist_response(response):
    ...

def snapshot(request, route):
    snapshot_file = ...  # find existing file by name based on request hash (url, etc details) or hashed route pattern?
    if response_file:
        # load response details from file, e.g. status, headers, body etc
        response = httpx.Response(status_code, headers=..., content=..., ...)
        # make route always return this response, i.e. no need to load file for subsequent requests
        route.mock(return_value=response)
        return response

    # Snapshot file does not exist, make request pass through
    # and register a response callback to persist it for subsequent requests
    return PassThrough(response_hook=persist_response)

respx.route(host="example.org").mock(side_effect=snapshot)

Note that the PassThrough already exists and should be enhanced. I'm also using the route arg feature from not yet merged #158.

Once we support the response callback hook mechanism we can either document this, or probably include this snapshot function as a built in side effect for optional use.

More ideas welcome 😉 .

@lundberg
Copy link
Owner

Ping @StephenBrown2 😉 ...your old #16 are kind-of being re-visited...any thoughts?

@lundberg
Copy link
Owner

Thoughts @sidharthv96 ?

@sidharthv96
Copy link
Author

Wow, this is Exactly what I was looking for!!
If you can give some pointers, I can try to take a shot at enhancing the pass through behavior to make this work.

@lundberg
Copy link
Owner

lundberg commented Aug 27, 2021

Yes, I think that we should break this issue down into separate pull request, where the first one is responsible for the pass through callback hook enhancement.

One alternative solution to the return PassThrough(...) idea would be to make use of the HTTPX client response hook mechanism, but haven't looked at how we could intercept/modify those yet. But it would be nice if possible since it could probably also fix #126 by recording the RESPX stats for pass-through requests in a hook like that, maybe even move all stats recording to a hook like that.

@lundberg
Copy link
Owner

If you can give some pointers, I can try to take a shot at enhancing the pass through behavior to make this work.

If we go with return PassThrough(...) solution, it would probably affect the Route._call_side_effect, Route.match and Router.resolver. Also the mockers needs to take care of calling the callback/hook.

I'll see if I can come up with a rough draft.

@lundberg
Copy link
Owner

A note/fyi....when looking at how we mock and how to implement a pass through response hook, it will have to callback using the transport result, i.e. raw url, stream, etc... not httpx response objects.

@Mojken
Copy link

Mojken commented Nov 11, 2021

What is the status on this? It looks like work has been done on this, opening a draft pull-request would make it easier for people to pick up from where this was left.

@lundberg
Copy link
Owner

opening a draft pull-request would make it easier for people to pick up from where this was left.

Agree, a draft would be good 😉

@StephenBrown2
Copy link

StephenBrown2 commented Nov 11, 2021

@lundberg Sorry, I've changed jobs and am no longer using Python as my main dev language anymore, and I don't believe I got it fully working, but @Mojken should look at the schema that VCRpy uses as a start, which is what I did.

@lundberg
Copy link
Owner

See you in the future @StephenBrown2

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

No branches or pull requests

4 participants