0

I'm writing unit tests for a requests library. For most of my libraries, I run a .content.decode('utf-8') on the response to get to the actual text. However when I'm mocking this response from the api call, how do I mock a response object which is received by the requests call? is there a requests.content.encode('utf-8',data)) look alike process which can actually mock the response coming in (in encoded format) from the API call, and then I decode that object.

sample code:

def sample_fct(self, endpoint):
    try:
        request = requests.post (endpoint, verify=False)
    except requests.exceptions.RequestException as e:
        raise
    return request

def get_something(self,test):
    try:
        response = self.sample_fct(test)
        resp_text = response_bare.content.decode('utf-8')
        print resp_text
    except:
        raise 

So for instance, if I wanted to unit test the get_something function, I need to mock the sample_fct function. to do this, i would have to set the sample_fct.return_value to a request object that it is returning.

So how do I create that object.

user2883071
  • 892
  • 1
  • 14
  • 37
  • Have you check the packages [requests-mock](https://requests-mock.readthedocs.io/en/latest/)? It is very useful when mocking `requests` calls. – Klaus D. Nov 14 '18 at 21:50
  • Can we see an example of your current mocking? I can write up a better example of how it should look if I have a starting point. Essentially you'd just assign the response (or a portion of a mocked response) to an already encoded value. You can either do that with raw bytes like this: `b'\xE2\x98\xBA'` or by starting with the encoded string and call `encode` like this: `'☺'.encode('utf-8')` – wholevinski Nov 15 '18 at 12:07
  • What should `requests.content.encode('utf-8',data))` do? – Stop harming Monica Nov 15 '18 at 14:21
  • @wholevinski added a sample – user2883071 Nov 15 '18 at 21:03
  • 2
    You can assign arbitrary attributes to a `Mock`, so it'd be something like: `mock_fct_return = mock.Mock(content=b'some content you want to decode')` – wholevinski Nov 15 '18 at 21:52

1 Answers1

1

Here's how you can mock the response like I was saying above. Note that I took some liberties to make your code and my test actually work. They are included in comments:

"""request_module.py"""
import requests


class SomeClass(object):
    """I put these in a class, since you had self as your first arg"""
    def sample_fct(self, endpoint):
        try:
            request = requests.post (endpoint, verify=False)
        except requests.exceptions.RequestException as e:
            raise
        return request

    def get_something(self, test):
        try:
            response = self.sample_fct(test)
            resp_text = response.content.decode('utf-8')
            # Changed to return the value instead of print it
            return resp_text
        except:
            raise 

You can see in the test how I assign the return value of your mocked sample_fct to an arbitrary Mock object. That object can have arbitrary attributes assigned to it, which can aid in testing for return values that may be many attribute levels deep.

"""test_request_module.py"""
import unittest
import unittest.mock as mock

from request_module import SomeClass

class TestRequests(unittest.TestCase):
    @mock.patch('request_module.SomeClass.sample_fct')
    def test_get_something(self, mock_sample_fct):
        mock_sample_fct.return_value = mock.Mock(content=b'some content you want to decode \xE2\x98\xBA')
        # Testing that your decoded value is returned
        some_class = SomeClass()
        result = some_class.get_something('some_test_value')
        # Note the decoded smiley face
        self.assertEqual('some content you want to decode ☺', result)

Note that you can also assign attributes of attributes on a Mock. So if your caller is expecting something like request.attributes.nested_attributes.value, you could do something like this and Mock will allow it:

mock_ret = mock.Mock()
mock_ret.request.attributes.nested_attributes.value = 'deep attr value'
wholevinski
  • 2,978
  • 13
  • 19
  • Just a follow up question.. the `.content.decode`, which library is that from? I thought it was from the requests library.. but it turns out it isn't? (I didnt import requests, and was still able to use `content.decode` – user2883071 Nov 17 '18 at 14:35
  • `.content` is an attribute on the response object coming back from the `requests` library. `.decode` is a function on strings. `.content` is in raw byte form, so using `decode` will take those bytes and turn them back into the intended representation (perhaps a UTF8 smiley face like above). Whenever you see a `u` before a string in python 2.7, that's a unicode string; the default strings in python 3 are already unicode. I could go on for awhile about this; I'd recommend checking out: https://stackoverflow.com/questions/15092437/python-encoding-utf-8 – wholevinski Nov 17 '18 at 14:43