I believe in Test-Driven Development but had somehow had never gotten around to using mock objects until a few months ago. They’re super-useful when testing classes that write to files or query remote databases or what-have-you, or when the rest of your system is big and hairy and setting up tests takes ridiculously more work than the test itself.

Python has a nifty third-party package called Mock that supplies a mock-object class and a method decorator patch that can be used to patch existing objects with mocks. Here’s an example from the tests for Texturepacker:

import unittest
from mock import Mock, patch
…

class ExternalResourceTests(TestCase):
    …
    @patch('__builtin__.open')
    def test_internal_url(self, mock_open):
        mock_open.return_value = StringIO('fish')
        url = 'minecraft:texturepacks/foo.tprx'
        spec = self.loader.maybe_get_spec({'href': url}, base=None)
        self.assertEqual('fish', spec)
        self.assertTrue(mock_open.called)

        expected_path = os.path.join(minecraft_texture_pack_dir_path(), 'foo.tprx')
        self.assertEqual(expected_path, mock_open.call_args[0][0])
        self.assertTrue(mock_open.call_args[0][1].startswith('r'))

Here I wanted to see whether the Loader object opens the correct file—even though the file and directory in question might not exist on some systems. By replacing the built-in open function with a fake (mock_open) I can make it return a known value and also check that the function was called and with the args as expected.

To understand the pun in the headline, you need to know that we often use ‘mock’ as a verb meaning replacing a real object with a mock one—and that, strictly speaking, some of our mocks are really stubs.

Yesterday I was working on code that fetches billing information for users, adding checks that the bills are current (rather than being three randomly selected bills, or old bills because the account has been closed).

One way to test this would be to create my test data using datetime.now() + timedelta(days=-30) and suchlike so that the dates are all relative to today’s date. This makes the tests very cluttered and hard to read. (Remember one of the purposes of unit tests is documenting the interface to the code.) So instead with a little digging I managed to patch the time.time library function with a stub:

import unittest
from mock import *
from datetime import datetime, timedelta
import time

mock_time = Mock()
mock_time.return_value = time.mktime(datetime(2011, 6, 21).timetuple())

class TestCrawlerChecksDates(unittest.TestCase):
    @patch('time.time', mock_time)
    def test_mock_datetime_now(self):
        self.assertEqual(datetime(2011, 6, 21), datetime.now())

Voilà! I mock time!

Author
X
Published
3 September
Topics