csernazs/pytest-httpserver

propagate assertion errors from custom handlers

thrau opened this issue · 2 comments

thrau commented

I would like to perform fuzzy assertions on the request body. Since that doesn't seem possible with the default RequestHandler, I added my own handler that does the assertions, and would like that assertion errors produced in these handlers are propagated to the pytest run via httpserver.check_assertions().

What I've currently done is:

my test:

def assert_appender(httpserver, handler):
    def _handler(response):
        try:
            return handler(response)
        except AssertionError as e:
            httpserver.add_assertion(e)
            raise

    return _handler


def test_append(httpserver: HTTPServer):
    def handler(request):
        actual_data = request.data.decode()
        assert 'hello' in actual_data
        # ... more asserts
        return Response('', 200)

    httpserver.expect_request("/path").respond_with_handler(assert_appender(httpserver, handler))

    httpserver.check_assertions()  # should re-raise the assertion error from "handler"
    assert response.ok

and i've modified this bit here:

def check_assertions(self):
"""
Raise AssertionError when at least one assertion added
The first assertion added by :py:meth:`add_assertion` will be raised and
it will be removed from the list.
This method can be useful to get some insights into the errors happened in
the sever, and to have a proper error reporting in pytest.
"""
if self.assertions:
raise AssertionError(self.assertions.pop(0))

to do the following:

        if self.assertions:
            assertion = self.assertions.pop(0)
            if isinstance(assertion, AssertionError):
                raise assertion
            raise AssertionError(assertion)

now i get the proper assertion formatting in pytest:

    def handler(request):
        actual_data = request.data.decode()
>       assert "hello" in actual_data
E       assert 'hello' in '{"foo":"bar"}'

test_http.py:34: AssertionError

would this be interesting to add? i'm happy to contribute a PR

Yes, this sounds to be an excellent idea. It is very cool that pytest is able to format the assertion in that nice format, showing the variables, etc. :)

If you want to volunteer, you are free to proceed with the PR. :)

I have a few thoughts about the implementation:

  • I'm thinking about the effect of adding a catch all exception handler so every exception (including AssertionError of course) would be re-raised in the check_assertions() call. But I'm unsure about how it would break the code of the users. What do you think?

  • The request matcher objects (RequestMatcher and RequestHandler) are not receiving the server object. So basically there are two ways to solve it (in my view):

    1. add the assert_appender function definition to respond_with_handler method, so it will wrap the given callable
    2. add the server object to the RequestMatcher object in the create_matcher method. This could be an API breaking point.

For me the 1st option is better, but if API breaking can be managed (eg. adding a kwarg which is optional) that would be also ok.

FYI: pytest-httpserver 1.0.1 has been released to pypi with your changes. Thanks for your contribution! 👍