AutoMockMixin

It’s been my experience that when you’re writing a TestCase that tests a specific function and that function needs to mock one (or more) of the functions that it uses, that function needs to be mocked throughout the entirety of the TestCase. The AutoMockMixin helps alleviate the burden this can cause.

mock.patch is typically used for most cases as a decorator on the test function like this:

# tests/test_foo.py
from unittest import mock

from tests import BaseTestCase

from myproject import foo


class TestFoo(BaseTestCase):
    @mock.patch('myproject.bar')
    def test_foo(self, mocked_bar):
        mocked_bar.return_value = 'baz'
        self.assertTrue(foo())

This works nicely when their is only a single function to mock and is even better if only one test needs to mock. However, if every test needs to mock and if each test needs more than one function to mock writing all of those mock.patch decorators can become hairy:

# tests/test_foo.py

class TestFoo(BaseTestCase):
    @mock.patch('myproject.bar')
    @mock.patch('myproject.baz')
    @mock.patch('myproject.biz')
    @mock.patch('myproject.boom')
    def test_1(self, bar, baz, biz, boom):
        # ...

    @mock.patch('myproject.bar')
    @mock.patch('myproject.baz')
    @mock.patch('myproject.biz')
    @mock.patch('myproject.boom')
    def test_2(self, bar, baz, biz, boom):
        # ...

    # ...

It’s for times like this that you can return value of mock.patch to set up mocking in setUp() and tearDown():

# tests/test_foo.py

class TestFoo(BaseTestCase):
    def setUp(self):
        super(TestFoo, self).setUp()
        self.bar_patcher = mock.patch('myproject.bar')
        self.bar = self.bar_patcher.start()

        self.baz_patcher = mock.patch('myproject.baz')
        self.baz = self.bar_patcher.start()

        self.biz_patcher = mock.patch('myproject.biz')
        self.biz = self.bar_patcher.start()

    def tearDown(self):
        super(TestFoo, self).tearDown()
        self.bar_patcher.stop()
        self.bar_patcher = None
        self.bar = None

        self.baz_patcher.stop()
        self.baz_patcher = None
        self.baz = None

        self.biz_patcher.stop()
        self.biz_patcher = None
        self.biz = None

    def test_1(self):
        self.bar.return_value = 'baz'
        self.baz.return_value = True
        self.biz.return_value = 'asdf'
        self.AssertTrue(foo())

This kind of a setup only requires you to declare the mocked function once, but even this is fairly tedious. This is where the AutoMockMixin comes in handy. Using it, the above to examples can be rewritten as:

# tests/test_foo.py
from granite.testcase import AutoMockMixin

from tests import BaseTestCase

class TestFoo(AutoMockMixin, BaseTestCase):
    bar = mock.patch('myproject.bar')
    baz = mock.patch('myproject.baz')
    biz = mock.patch('myproject.biz')
    boom = mock.patch('myproject.boom')

    def test_1(self):
        self.bar.return_value = 'baz'
        self.baz.return_value = True
        self.biz.return_value = 'asdf'
        self.AssertTrue(foo())

Ahhh, much nicer. We no longer have to worry about managing the state of mock patching setup and teardown as the mixin does all of that work for us!

Note

The AutoMockMixin not only works with mock.patch, but it also works with mock.dict and mock.object.