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.
1 Answer 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'
2 Comments
.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.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: stackoverflow.com/questions/15092437/python-encoding-utf-8
requestscalls.b'\xE2\x98\xBA'or by starting with the encoded string and callencodelike this:'☺'.encode('utf-8')requests.content.encode('utf-8',data))do?Mock, so it'd be something like:mock_fct_return = mock.Mock(content=b'some content you want to decode')