4

I am struggling with mocking attributes in my Python tests. The function I am trying to test keeps failing because the mock probably returns the right value but is the wrong type (Should be string and it is a MagicMock instead.

I have found this answer and I understand I need to use a PropertyMock. But I can't get it to work neither with the context manager or using the @patch decorator. Mock attributes in Python mock?

Here is my test:

 @patch('keys.views.requests.post')
 @patch('keys.views.requests.Response.text', new_callable=PropertyMock)
 def test_shows_message_when_receives_error(self, mock_response_text     ,mock_post):

     expected_error = escape(MESSAGE)
     data_to_be_received = json.dumps({
         "message":"Bad Request",
         "errors":[{
             "resource":"Application",
             "field":"client_id",
             "code":"invalid"
         }]
     })
     mock_response_text.return_value = data_to_be_received

     response = self.client.get('/users/tokenexchange?state=&code=abc123')

     self.assertContains(response, expected_error)

And the code I am testing:

def token_exchange(request):
    parameters = {'client_id': '##', 'client_secret': '##', 'code': code}

    response = requests.post('https://www.strava.com/oauth/token', parameters)
    data_received = json.loads(response.text)

    if 'errors' not in data_received:
        return HttpResponse(response.text)
     else:
         return render(request, 'home.html', {'error': STRAVA_AUTH_ERROR})

The error I keep getting is:

File "##", line 66, in token_exchange
    data_received = json.loads(response.text)

TypeError: the JSON object must be str, bytes or bytearray, not 'MagicMock'
hewi
  • 1,274
  • 3
  • 17
  • 32
Alvaro Aguilar
  • 694
  • 8
  • 23

1 Answers1

3

keys.views.requests.Response.text is highly likely a instance variable which cannot and should not be mocked using PorpertyMock

Here is quote from documentation:

Generally speaking, instance variables are for data unique to each instance and class variables are for attributes and methods shared by all instances of the class:

https://docs.python.org/2/tutorial/classes.html

class Dog:

    kind = 'canine'         # class variable shared by all instances

    def __init__(self, name):
        self.name = name    # instance variable unique to each instance

How to mock a python class instance_variable?

I had a solution which copied from somewhere, it worked by too tedious:

Python mock a base class's attribute

In your specific case, mock Response class instead of the text instance

@patch('keys.views.requests.post')
@patch('keys.views.requests.Response')
def test_shows_message_when_receives_error(self, mock_response ,mock_post):
    mock_post.return_value = None # mock the func
    mock_response.text = mock.Mock(text=data_to_be_received).text
Gang
  • 2,658
  • 3
  • 17
  • 38