# Testing Helpers

The `treq.testing` module provides some tools for testing both HTTP clients which use the treq API and implementations of the Twisted Web resource model.

## Writing tests for HTTP clients

The `StubTreq` class implements the `treq` module interface (`treq.get()`, `treq.post()`, etc.) but runs all I/O via a `MemoryReactor`. It wraps a `twisted.web.resource.IResource` provider which handles each request.

You can wrap a pre-existing `twisted.web.resource.IResource` provider, or write your own. For example, the `twisted.web.resource.ErrorPage` resource can produce an arbitrary HTTP status code. `twisted.web.static.File` can serve files or directories. And you can easily achieve custom responses by writing trivial resources yourself:

     1@implementer(IResource)
     2class JsonResource:
     3    isLeaf = True  # NB: means getChildWithDefault will not be called
     4
     5    def __init__(self, data):
     6        self.data = data
     7
     8    def render(self, request):
     9        request.setHeader(b'Content-Type', b'application/json')
    10        return json.dumps(self.data).encode('utf-8')

However, those resources don’t assert anything about the request. The `RequestSequence` and `StringStubbingResource` classes make it easy to construct a resource which encodes the expected request and response pairs. Do note that most parameters to these functions must be bytes—it’s safest to use the `b''` string syntax, which works on both Python 2 and 3.

For example:

     1from twisted.internet import defer
     2from twisted.trial.unittest import SynchronousTestCase
     3from twisted.web import http
     4
     5from treq.testing import StubTreq, HasHeaders
     6from treq.testing import RequestSequence, StringStubbingResource
     7
     8
     9@defer.inlineCallbacks
    10def make_a_request(treq):
    11    """
    12    Make a request using treq.
    13    """
    14    response = yield treq.get('http://an.example/foo', params={'a': 'b'},
    15                              headers={b'Accept': b'application/json'})
    16    if response.code == http.OK:
    17        result = yield response.json()
    18    else:
    19        message = yield response.text()
    20        raise Exception(f"Got an error from the server: {message}")
    21    return result
    22
    23
    24class MakeARequestTests(SynchronousTestCase):
    25    """
    26    Test :func:`make_a_request()` using :mod:`treq.testing.RequestSequence`.
    27    """
    28
    29    def test_200_ok(self):
    30        """On a 200 response, return the response's JSON."""
    31        req_seq = RequestSequence([
    32            ((b'get', 'http://an.example/foo', {b'a': [b'b']},
    33              HasHeaders({'Accept': ['application/json']}), b''),
    34             (http.OK, {b'Content-Type': b'application/json'}, b'{"status": "ok"}'))
    35        ])
    36        treq = StubTreq(StringStubbingResource(req_seq))
    37
    38        with req_seq.consume(self.fail):
    39            result = self.successResultOf(make_a_request(treq))
    40
    41        self.assertEqual({"status": "ok"}, result)
    42
    43    def test_418_teapot(self):
    44        """On an unexpected response code, raise an exception"""
    45        req_seq = RequestSequence([
    46            ((b'get', 'http://an.example/foo', {b'a': [b'b']},
    47              HasHeaders({'Accept': ['application/json']}), b''),
    48             (418, {b'Content-Type': b'text/plain'}, b"I'm a teapot!"))
    49        ])
    50        treq = StubTreq(StringStubbingResource(req_seq))
    51
    52        with req_seq.consume(self.fail):
    53            failure = self.failureResultOf(make_a_request(treq))
    54
    55        self.assertEqual("Got an error from the server: I'm a teapot!",
    56                         failure.getErrorMessage())

This may be run with `trial testing_seq.py`. Download: `testing_seq.py`.

### Loosely matching the request

If you don’t care about certain parts of the request, you can pass `unittest.mock.ANY`, which compares equal to anything. This sequence matches a single GET request with any parameters or headers:

    from unittest.mock import ANY

    RequestSequence([
        ((b'get', ANY, ANY, b''), (200, {}, b'ok'))
    ])

If you care about headers, use `HasHeaders` to make assertions about the headers present in the request. It compares equal to a superset of the headers specified, which helps make your test robust to changes in treq or Agent. Right now treq adds the `Accept-Encoding: gzip` header, but as support for additional compression methods is added, this may change.

## Writing tests for Twisted Web resources

Since `StubTreq` wraps any resource, you can use it to test your server-side code as well. This is superior to calling your resource’s methods directly or passing mock objects, since it uses a real `Agent` to generate the request and a real `Site` to process the response. Thus, the `request` object your code interacts with is a real `twisted.web.server.Request` and behaves the same as it would in production.

Note that if your resource returns `NOT_DONE_YET` you must keep a reference to the `RequestTraversalAgent` and call its `flush()` method to spin the memory reactor once the server writes additional data before the client will receive it.