falconry / falcon

The no-magic web data plane API and microservices framework for Python developers, with a focus on reliability, correctness, and performance at scale.
https://falcon.readthedocs.io/en/stable/
Apache License 2.0
9.51k stars 944 forks source link

Alternative approach to testing by creating arbitrary req/resp/ws #1793

Open vytas7 opened 3 years ago

vytas7 commented 3 years ago

New methods were introduced as part of https://github.com/falconry/falcon/issues/1506 that afford an alternative approach to testing by creating arbitrary req/resp/ws:

Write a paragraph on how to use these goodies. From https://github.com/falconry/falcon/issues/1506:

def test_my_resource():
    resource = MyResource()
    req, resp = Request(helpers.create_environ()), Response()
    resource.on_get(req, resp)

Also check if we want to recommend instantiating Response directly, or we want to provide a wrapper method for that too.

(Requirements transplanted from https://github.com/falconry/falcon/issues/1506)

def create_test_request_response(json: dict = None, body: string = None, context: dict = None, **env):
    env['body'] = (
        (body if body is not None else None)
        or (json.dumps(json) if json else '')
    )

    env = falcon.testing.create_environ(**env,)
    req = falcon.Request(env)
    req.context.update(context or {})
    return (req, falcon.Response())

def test_my_resource():
    resource = MyResource()
    req, resp = create_test_request_response(json={'hello': 'world'})
    resource.on_get(req, resp)
CaselIT commented 3 years ago

Provide a global method in testing.helpers which creates a Request and Response object.

I think it could also be useful if we also allow setting the context with this function. Maybe by passing a dict that gets translated in the context object.

vytas7 commented 10 months ago

@CaselIT maybe it would be enough to document the context part? One can operate on the created req or resp as usual, i.e.:

>>> req = falcon.testing.create_asgi_req(path='/things', method='PATCH')
>>> req.context.key = 'value'
>>> req.context
Context({'key': 'value'})
CaselIT commented 10 months ago

sure that works too, but it shouldn't be too hard to allow context to be passed as argument (unless **kw is already sent to another mapping where context may be a key used by people)

vytas7 commented 10 months ago

I see, that might make sense. I don't think we should pipe **kw there though, because that might be ambiguous with setting env/scope key-values. But we can add another mapping argument.

CaselIT commented 10 months ago

no, I just meant that it should be fine to add a context=None there, unless we take **kw and use it to set another mapping. If that's the case then this would be a backward incompatible change since people could be using context to pass data somewhere else.

If that's the case then best course of action is to either not have it or use another keyword that's more unlikely, like requext_context or something

vytas7 commented 10 months ago

Ah, no, these functions are incomplete (cannot set body, json, etc) and lack examples, it is absolutely not a breaking change to add things there.

iuliachiriac commented 3 months ago

I started looking into this (during EuroPython sprints) 😄