Scott Woodall
Scott Woodall

Reputation: 10676

Django unittest and mocking the requests module

I am new to Mock and am writing a unit test for this function:

# utils.py
import requests    

def some_function(user):
    payload = {'Email': user.email}
    url = 'http://api.example.com'
    response = requests.get(url, params=payload)      

    if response.status_code == 200:
       return response.json()
    else:
        return None

I am using Michael Foord's Mock library as part of my unit test and am having difficulty mocking the response.json() to return a json structure. Here is my unit test:

# tests.py
from .utils import some_function

class UtilsTestCase(unittest.TestCase):
    def test_some_function(self):
        with patch('utils.requests') as mock_requests:
            mock_requests.get.return_value.status_code = 200
            mock_requests.get.return_value.content = '{"UserId":"123456"}'
            results = some_function(self.user)
            self.assertEqual(results['UserId'], '123456')

I have tried numerous combinations of different mock settings after reading the docs with no luck. If I print the results in my unit test it always displays the following instead of the json data structure I want:

<MagicMock name=u'requests.get().json().__getitem__().__getitem__()' id='30315152'>

Thoughts on what I am doing wrong?

Upvotes: 10

Views: 11418

Answers (3)

Hassek
Hassek

Reputation: 8995

Another way that I believe is more clear and straight forward:

import unittest

from mock import Mock, patch

import utils

class UtilsTestCase(unittest.TestCase):
    def test_some_function(self):
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {"UserId": "123456"}
        with patch('utils.requests.get') as mock_requests:
            results = utils.some_function(self.user)
            self.assertEqual(results['UserId'], '123456')

Upvotes: 0

adam
adam

Reputation: 6738

Another pattern I like to use that is a little more reusable would be to start the patcher in your unit test's setUp method. It's also important to check that mock request was called with the expected parameters:

class UtilsTestCase(TestCase):

    def setUp(self):
        self.user = Mock(id=123, email='[email protected]')

        patcher = patch('utils.requests.get')
        self.mock_response = Mock(status_code=200)
        self.mock_response.raise_for_status.return_value = None
        self.mock_response.json.return_value = {'UserId': self.user.id}
        self.mock_request = patcher.start()
        self.mock_request.return_value = self.mock_response

    def tearDown(self):
        self.mock_request.stop()

    def test_request(self):
        results = utils.some_function(self.user)

        self.assertEqual(results['UserId'], 123)

        self.mock_request.assert_called_once_with(
            'http://api.example.com'
            payload={'Email': self.user.email},
        )

    def test_bad_request(self):
        # override defaults and reassign
        self.mock_response.status_code = 500
        self.mock_request.return_value = self.mock_response
        results = utils.some_function(self.user)

        self.assertEqual(results, None)

        self.mock_request.assert_called_once_with(
            'http://api.example.com'
            payload={'Email': user.email},
        )

Upvotes: 2

falsetru
falsetru

Reputation: 368894

Patch json method instead of content. (content is not used in some_function)

Try following code.

import unittest

from mock import Mock, patch

import utils

class UtilsTestCase(unittest.TestCase):
    def test_some_function(self):
        user = self.user = Mock()
        user.email = '[email protected]'
        with patch('utils.requests') as mock_requests:
            mock_requests.get.return_value = mock_response = Mock()
            mock_response.status_code = 200
            mock_response.json.return_value = {"UserId":"123456"}
            results = utils.some_function(self.user)
            self.assertEqual(results['UserId'], '123456')

Upvotes: 17

Related Questions