55

For testing purposes I'm trying to create a Response() object in python but it proves harder then it sounds.

i tried this:

from requests.models import Response

the_response = Response()
the_response.code = "expired"
the_response.error_type = "expired"
the_response.status_code = 400

but when I attempted the_response.json() i got an error because the function tries to get len(self.content) and a.content is null. So I set a._content = "{}" but then I get an encoding error, so I have to change a.encoding, but then it fails to decode the content.... this goes on and on. Is there a simple way to create a Response object that's functional and has an arbitrary status_code and content?

Dotan
  • 6,602
  • 10
  • 34
  • 47
  • Have you considered using something like [`responses`](https://github.com/getsentry/responses)? Alternatively, create a [`mock`](https://docs.python.org/3/library/unittest.mock.html) rather than trying to recreate the real object – jonrsharpe Nov 01 '16 at 13:47

5 Answers5

101

That because the _content attribute on the Response objects (on python3) has to be bytes and not unicodes.

Here is how to do it:

from requests.models import Response

the_response = Response()
the_response.code = "expired"
the_response.error_type = "expired"
the_response.status_code = 400
the_response._content = b'{ "key" : "a" }'

print(the_response.json())
Or Duan
  • 13,142
  • 6
  • 60
  • 65
  • 4
    This will work, but breaks if `requests` changes the implementation of `Response`; the leading underscore indicates an internal detail that shouldn't be relied upon. That said, it hasn't changed for a while (and such a change may also affect how `responses` does its thing, unless it mocks the public interface too). – jonrsharpe Nov 01 '16 at 15:04
  • 1
    100% agreed, but as for OP question - this is the right solution. Also upvoted your mock solution(much better than 3th party lib), but again the OP asked for specific use case and wanted to know why his code is not working. – Or Duan Nov 01 '16 at 15:08
  • Incidentally, why start with `Response()`? Can't you provide some of those parameters like `Response(data={...}, status=code)` etc? – NeilG Apr 21 '22 at 06:15
  • 1
    @NeilG the `requests.models.Response` class does not take parameters during initialization. See - https://github.com/psf/requests/blob/main/requests/models.py#L659 – zinglax Apr 04 '23 at 18:47
46

Create a mock object, rather than trying to build a real one:

from unittest.mock import Mock

from requests.models import Response

the_response = Mock(spec=Response)

the_response.json.return_value = {}
the_response.status_code = 400

Providing a spec ensures that the mock will complain if you try to access methods and attributes a real Response doesn't have.

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
3

Just use the responses library to do it for you:

import responses

@responses.activate
def test_my_api():
    responses.add(responses.GET, 'http://whatever.org',
                  json={}, status=400)

    ...

This has the advantage that it intercepts a real request, rather than having to inject a response somewhere.

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
1

Same as what was in the accepted answer but you can use the raw attribute instead of the _content private attribute (as that is an internal detail of the library):

from io import BytesIO
from requests.models import Response

the_response = Response()
the_response.code = "expired"
the_response.error_type = "expired"
the_response.status_code = 400

the_response.raw = BytesIO(b'{ "key" : "a" }')

print(the_response.json())
Nihhaar
  • 141
  • 11
0

Another approach by using the requests_mock library, here with the provided fixture:

import requests


def test_response(requests_mock):
    requests_mock.register_uri('POST', 'http://test.com/', text='data', headers={
        'X-Something': '1',
    })
    response = requests.request('POST', 'http://test.com/', data='helloworld')

    ...
AngryUbuntuNerd
  • 760
  • 8
  • 8