Python Mock Functions Not Being Called

Are you using mock to mock some functions during testing and mysteriously your tests are ignoring the mock decorator?

One way this can happen is if the function being mocked is either defined in the module that is being tested or is imported into the module being tested. How to get around this?

In my case, after I wasted time figuring out why mock was not working, I ended up modifying the code in my module so that I did not need mock. In the end, I think the modified code is better anyway. Often making code testable, makes it better too.

Advertisements

Mocking Django Timezone

I use django.utils.timezone.now() in a lot of my code. It returns the datetime in the timezone specified in settings.py. One side effect of using this function is that during testing, you may want “now()” to be a fixed date time. No worries. Mock to the rescue. Here is the code:

import datetime

from django.test import TestCase
from django.utils import timezone

import mock

# Make now() a constant
NOW_FOR_TESTING = datetime.datetime(2015, 10, 10, 10)

# This is the function that replaces django.utils.timezone.now()
def mocked_now():
    return NOW_FOR_TESTING

# This function shows that the mocking is in effect even outside of the TestMyTest scope.
def a_func():
    return timezone.now()

@mock.patch('django.utils.timezone.now', side_effect=mocked_now)
class TestMyTest(TestCase):
    def test_time_zone(self, *args):
        # After patching, mock passes in some extra vars. Put *args to handle them.
        self.assertEqual(timezone.now(), NOW_FOR_TESTING)
        self.assertEqual(timezone.now().date(), NOW_FOR_TESTING.date())
        self.assertEqual(mocked_now(), NOW_FOR_TESTING)

Note that you need to add the “*args” argument to the methods in your TestCase class.

Django Testing using Mock

Two important 3rd party testing tools for Django are Mock and Mommy. Both allow you to quickly create instances of Django models for testing. The advantage of Mock is it is fast because it does not require a test database to be setup. It is used primarily for unit testing.

Here is a skeleton of a mock test:

import unittest
import mock

from models import MyModel

class TestMyCode(unittest.TestCase):
    def test_1(self):
        instance = mock.Mock(spec=MyModel)
        # Do something with instance

Put this in your tests.py file for the app. Then run using:

python manage.py test my_app.TestMyCode