Skip to the content.

Mocking a method call

Imagine a method that makes an HTTP request. Let’s call him MethodX.

MethodX is being called from another method. Guess what.. this another method will be called MethodY.

If we are testing MethodY, can we mock MethodX so our test doesn’t make the HTTP request?

Of Course

In this example we are going to mock a GET HTTP request that fetches a list of countries from a world population API.

Also, we will assert if our method is calling the right url http://api.population.io/1.0/countries.

With that in mind, we disobey TDD write our method to be tested before the test itself:

def get_countries():
    countries_url = 'http://api.population.io/1.0/countries'
    request_response = requests.get(countries_url).json()
    return request_response['countries']

Look at this filthy piece of code: requests.get(countries_url).json().

This is where the HTTP request will be made.

How can we mock it?

Using patch

Using patch as decorator

You will use the @patch('package.module.method') decorator just before the test method. This will create the mock that will be passed as parameter to your test function.

import unittest
from unittest.mock import patch

from examples.mocking_a_method_call.get_countries import get_countries


class TestGetCountriesWithPatchAsDecorator(unittest.TestCase):
    @patch('examples.mocking_a_method_call.get_countries.requests.get')
    def test_get_countries(self, mock_http_get):
        expected_countries = ['Brazil', 'The rest of the world']
        fake_url = 'http://ima.fake.url'

        mock_http_get_response = mock_http_get.return_value
        mock_http_get_response.json.return_value = {
            'countries': expected_countries
        }

        actual_countries = get_countries(fake_url)

        mock_http_get.assert_called_once_with(fake_url)

        self.assertEqual(expected_countries, actual_countries)

Using a straight-forward patch


class TestGetCountriesWithStraightForward(unittest.TestCase):
    def setUp(self):
        patcher = patch('examples.mocking_a_method_call.get_countries.requests.get')
        self.mock_http_get = patcher.start()
        self.addCleanup(patch.stopall)

    def test_get_countries(self):
        expected_countries = ['Brazil', 'The rest of the world']
        fake_url = 'http://ima.fake.url'

        mock_http_get_response = self.mock_http_get.return_value
        mock_http_get_response.json.return_value = {
            'countries': expected_countries
        }

        actual_countries = get_countries(fake_url)

        self.mock_http_get.assert_called_once_with(fake_url)

        self.assertEqual(expected_countries, actual_countries)

Using patch as context manager

class TestGetCountriesWithContextManagerPatch(unittest.TestCase):
    def test_get_countries(self):
        with patch('examples.mocking_a_method_call.get_countries.requests.get') as mock_http_get:
            expected_countries = ['Brazil', 'The rest of the world']
            fake_url = 'http://ima.fake.url'

            mock_http_get_response = mock_http_get.return_value
            mock_http_get_response.json.return_value = {
                'countries': expected_countries
            }

            actual_countries = get_countries(fake_url)

            mock_http_get.assert_called_once_with(fake_url)

        self.assertEqual(expected_countries, actual_countries)

See the source code

So many ways to mock…

Yeah, there is.

Actually, all of this wibbly wobbly timey wimey stuff is centered on the unittest Mock object.

Further details here.

Also, see the official docs

What about now?