I'm writing an endpoint to receive and parse GitHub Webhook payloads 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.
However, the problem comes when trying to test the request generated with DRF's Request class. Here's the smallest failing test I could come up with - the problem is that a request generated with DRF's APIRequestFactory seems to not be parsable by DRF's Request class. Is that expected behaviour?
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') And the stack trace is:
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' ---------------------------------------------------------------------- I'm obviously doing something stupid - I've messed around with encodings... realised that I needed to pass the parsers list to the Request to avoid the UnsupportedMediaType error, and now I'm stuck here.
Should I do something different? Maybe avoid using APIRequestFactory? Or test my built GitHub requests a different way?
More info
GitHub sends a request out to registered webhooks that has a X-GitHub-Event header and therefore in order to test my webhook DRF code I need to be able to emulate this header at test time.
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:
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 The issue has arisen because I want to assert that PayloadRequestFactory is generating valid requests for various passed arguments - so I'm trying to parse them and assert their validity but DRF's Request class doesn't seem to be able to achieve this - hence my question with a failing test.
So really my question is - how should I test this PayloadRequestFactory is generating the kind of request that I need?