Test Requests for Django Rest Framework aren't parsable by its own Request class
<p>I'm writing an endpoint to receive and parse <a href="https://developer.github.com/webhooks/#example-delivery" rel="nofollow">GitHub Webhook payloads</a> using Django Rest Framework 3. In order to match the payload specification, I'm writing a payload request factory and testing that it's generating valid requests.</p>

<p>However, the problem comes when trying to test the request generated with DRF's <code>Request</code> class. Here's the smallest failing test I could come up with - the problem is that a request generated with DRF's <code>APIRequestFactory</code> seems to not be parsable by DRF's <code>Request</code> class. Is that expected behaviour?</p>

<pre><code>from rest_framework.request import Request
from rest_framework.parsers import JSONParser
from rest_framework.test import APIRequestFactory, APITestCase


class TestRoundtrip(APITestCase):

    def test_round_trip(self):
        """
        A DRF Request can be loaded into a DRF Request object
        """
        request_factory = APIRequestFactory()
        request = request_factory.post(
            '/',
            data={'hello': 'world'},
            format='json',
        )

        result = Request(request, parsers=(JSONParser,))

        self.assertEqual(result.data['hello'], 'world')
</code></pre>

<p>And the stack trace is:</p>

<pre><code>E
======================================================================
ERROR: A DRF Request can be loaded into a DRF Request object
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/james/active/prlint/venv/lib/python3.4/site-packages/rest_framework/request.py", line 380, in __getattribute__
    return getattr(self._request, attr)
AttributeError: 'WSGIRequest' object has no attribute 'data'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/james/active/prlint/prlint/github/tests/test_payload_factories/test_roundtrip.py", line 22, in test_round_trip
    self.assertEqual(result.data['hello'], 'world')
  File "/home/james/active/prlint/venv/lib/python3.4/site-packages/rest_framework/request.py", line 382, in __getattribute__
    six.reraise(info[0], info[1], info[2].tb_next)
  File "/home/james/active/prlint/venv/lib/python3.4/site-packages/django/utils/six.py", line 685, in reraise
    raise value.with_traceback(tb)
  File "/home/james/active/prlint/venv/lib/python3.4/site-packages/rest_framework/request.py", line 186, in data
    self._load_data_and_files()
  File "/home/james/active/prlint/venv/lib/python3.4/site-packages/rest_framework/request.py", line 246, in _load_data_and_files
    self._data, self._files = self._parse()
  File "/home/james/active/prlint/venv/lib/python3.4/site-packages/rest_framework/request.py", line 312, in _parse
    parsed = parser.parse(stream, media_type, self.parser_context)
  File "/home/james/active/prlint/venv/lib/python3.4/site-packages/rest_framework/parsers.py", line 64, in parse
    data = stream.read().decode(encoding)
AttributeError: 'str' object has no attribute 'read'

----------------------------------------------------------------------
</code></pre>

<p>I'm obviously doing something stupid - I've messed around with encodings... realised that I needed to pass the parsers list to the <code>Request</code> to avoid the <code>UnsupportedMediaType</code> error, and now I'm stuck here.</p>

<p>Should I do something different? Maybe avoid using <code>APIRequestFactory</code>? Or test my built GitHub requests a different way?</p>

<hr>

<h2>More info</h2>

<p>GitHub sends a request out to registered webhooks that has a <code>X-GitHub-Event</code> header and therefore in order to test my webhook DRF code I need to be able to emulate this header at test time.</p>

<p>My path to succeeding with this has been to build a custom Request and load a payload using a factory into it. This is my factory code:</p>

<pre><code>def PayloadRequestFactory():
    """
    Build a Request, configure it to look like a webhook payload from GitHub.
    """
    request_factory = APIRequestFactory()
    request = request_factory.post(url, data=PingPayloadFactory())
    request.META['HTTP_X_GITHUB_EVENT'] = 'ping'
    return request
</code></pre>

<p>The issue has arisen because I want to assert that <code>PayloadRequestFactory</code> is generating valid requests for various passed arguments - so I'm trying to parse them and assert their validity but DRF's <code>Request</code> class doesn't seem to be able to achieve this - hence my question with a failing test.</p>

<p>So really my question is - how should I test this <code>PayloadRequestFactory</code> is generating the kind of request that I need?</p>

<p>Looking at the <a href="https://github.com/tomchristie/django-rest-framework/blob/master/tests/test_testing.py" rel="nofollow">tests for <code>APIRequestFactory</code> in
DRF</a>, <a href="https://github.com/tomchristie/django-rest-framework/blob/master/tests/test_testing.py#L18-L23" rel="nofollow">stub
views</a>
are created and then run through that view - the output is inspected for expected results.
Therefore a reasonable, but slightly long solution, is to copy this strategy to
assert that the <code>PayloadRequestFactory</code> is building valid requests, before then
pointing that at a full view.</p>

<p>The test above becomes:</p>

<pre><code>from django.conf.urls import url
from django.test import TestCase, override_settings
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.test import APIRequestFactory


@api_view(['POST'])
def view(request):
    """
    Testing stub view to return Request's data and GitHub event header.
    """
    return Response({
        'header_github_event': request.META.get('HTTP_X_GITHUB_EVENT', ''),
        'request_data': request.data,
    })


urlpatterns = [
    url(r'^view/$', view),
]


@override_settings(ROOT_URLCONF='github.tests.test_payload_factories.test_roundtrip')
class TestRoundtrip(TestCase):

    def test_round_trip(self):
        """
        A DRF Request can be loaded via stub view
        """
        request_factory = APIRequestFactory()
        request = request_factory.post(
            '/view/',
            data={'hello': 'world'},
            format='json',
        )

        result = view(request)

        self.assertEqual(result.data['request_data'], {'hello': 'world'})
        self.assertEqual(result.data['header_github_event'], '')
</code></pre>

<p>Which passes :D</p>

python unit-testing django-rest-framework