4

I want to mock the requests.Response() object in my unit test, I got a hint from below link.

How to mock data as request.Response type in python

Here, I can just set the status_code value (which is not a @Property), I want to set the value for @Property text or content

class UsernamePasswordAuthStrategyTest(TestCase):

def test_do_success(self):
    content = self._factory.get_reader(ReaderType.CONTENT).read('MY_TEST')
    auth_strategy = UsernamePasswordAuthStrategy(content)
    # mock send_request method response
    response = Response()
    response.status_code = 200
    # How could I achieve below line? 
    response.text = """<html>
            <body>
                <form method="post" name="NavForm">
                    <input id="csrfKey" name="csrfKey" type="hidden" value="JEK7schtDx5IVcH1eOWKN9lFH7ptcwHD/"/>
                </form>
            </body>
        </html>"""
    auth_strategy.send_request = mock.MagicMock(return_value=response)
    session, auth_result = auth_strategy.do()  # {'for_next_url_params': {'csrfKey': 'T4WNcz+hXqrxVa5R9o2HXkDm8pNZEi4k/'}}
    self.assertTrue(session, 'Test Failed! Something went wrong')
    self.assertTrue('for_next_url_params' in auth_result and 'csrfKey' in auth_result['for_next_url_params'],
                    'Test Failed! csrfKey not found')

send_request returns the response

drt
  • 735
  • 6
  • 16
  • We need some of your code to work off please. – eagle33322 Jan 30 '19 at 17:36
  • @eagle33322 Provided link had enough information so didn't add code earlier. You could take an example in the provided link also – drt Jan 30 '19 at 17:48
  • Possible duplicate of [How to mock data as request.Response type in python](https://stackoverflow.com/questions/51678972/how-to-mock-data-as-request-response-type-in-python) – eagle33322 Jan 30 '19 at 17:50
  • @eagle33322 not really, that example just tells how to set value for `status_code` which is not @Property And the same link I have used in my description – drt Jan 30 '19 at 17:52

2 Answers2

9

text value may be set by using private property _content (it should be bytes):

import requests as r

res = r.Response()
res.status_code = 404

utf_string = '{"error": "page not found", "check UTF8 characters": "あア"}'
bytes_string = utf_string.encode('utf-8')
res._content = bytes_string

print('Status code:', res.status_code)
print('RAW:', f'[{type(res.content)}]', res.content)
print('Private RAW:', f'[{type(res._content)}]', res._content)
print('Decoded text:', f'[{type(res.text)}]', res.text)
print('JSON converted:', f'[{type(res.json())}]', res.json())

output:

Status code: 404
RAW: [<class 'bytes'>] b'{"error": "page not found", "check UTF8 characters": "\xe3\x81\x82\xe3\x82\xa2"}'
Private RAW: [<class 'bytes'>] b'{"error": "page not found", "check UTF8 characters": "\xe3\x81\x82\xe3\x82\xa2"}'
Decoded text: [<class 'str'>] {"error": "page not found", "check UTF8 characters": "あア"}
JSON converted: [<class 'dict'>] {'error': 'page not found', 'check UTF8 characters': 'あア'}
rzlvmp
  • 7,512
  • 5
  • 16
  • 45
  • 3
    This was a really useful answer. I think it is actually superior to the accepted answer since it appears to be simpler. A key piece of information you might consider highlighting (since it scrolls off the page in the code view) is the `.encode('utf-8')` on the 4th code line. Maybe assign the string to a variable on a different line and then assign _content to that variable. And then the encode call will be more visible. Just a thought. I missed it the first time when going through your answer. – joshmcode Dec 01 '21 at 18:19
  • This is a quick and dirty way to manually push a pre-loaded `.text` into the response for internal testing instead of loading a full API call and waiting 5 minutes for your servers to download data. – Nelson Feb 08 '22 at 12:56
  • Quick and simple, I like it – Greg7000 Oct 21 '22 at 13:10
8

I have gone through the python documentation and figured it out...

the solution is ->

def test_do_success(self):
        content = self._factory.get_reader(ReaderType.CONTENT).read('MY_TEST')
        auth_strategy = UsernamePasswordAuthStrategy(content)
        # mock send_request method response
        response = Response()
        response.status_code = 200
        my_text = """<html>
                <body>
                    <form method="post" name="NavForm">
                        <input id="csrfKey" name="csrfKey" type="hidden" value="JEK7schtDx5IVcH1eOWKN9lFH7ptcwHD/"/>
                    </form>
                </body>
            </html>
        """
        type(response).text = mock.PropertyMock(return_value=my_text)
        auth_strategy.send_request = mock.MagicMock(return_value=response)
        session, auth_result = auth_strategy.do()
        self.assertTrue(session, 'Test Failed! Something went wrong')
        self.assertTrue('JEK7schtDx5IVcH1eOWKN9lFH7ptcwHD' in auth_result['for_next_url_params']['csrfKey'],
                        'Test Failed! csrfKey not found')

I had add the PropertyMock around text, code change is -->

type(response).text = mock.PropertyMock(return_value=my_text)
drt
  • 735
  • 6
  • 16
  • Nice answer. I'd like to know how/why it works; could you add it to your answer or link to the documentation you mentioned? – Freemium May 19 '21 at 09:48
  • This solution redefine `text` attribute for `requests.Response` class. It may be checked by: `import requests as r; a = r.request('POST', 'https://www.google.com'); type(a).text = '{"k":"v"}'; a = r.request('POST', 'https://www.google.com'); print(a.text)`. Even after `a` will be reassigned text will be `'{"k":"v"}'` all times before restart process. – rzlvmp Nov 29 '21 at 05:35