Search code examples
pythonmockingpython-unittestpython-mock

How do you mock patch a python class and get a new Mock object for each instantiation?


OK,
I know this is mentioned in the manual, and probably has to do with side_effect and/or return_value, but a simple, direct example will help me immensely.

I have:

class ClassToPatch():
   def __init__(self, *args):
       _do_some_init_stuff()

   def some_func():
       _do_stuff()


class UUT():
    def __init__(self, *args)
       resource_1 = ClassToPatch()
       resource_2 = ClassToPatch()

Now, I want to unit test the UUT class, and mock the ClassToPatch. Knowing the UUT class will instantiate exactly two ClassToPatch objects, I want the Mock framework to return a new Mock object for each instantiation, so I can later assert calls on each separately.

How do I achieve this using the @patch decorator in a test case? Namely, how to fix the following code sample?

class TestCase1(unittest.TestCase):

    @patch('classToPatch.ClassToPatch',autospec=True)
    def test_1(self,mock1,mock2):
        _assert_stuff()

Solution

  • Here's a quick'n'dirty example to get you going:

    import mock
    import unittest
    
    class ClassToPatch():
       def __init__(self, *args):
           pass
    
       def some_func(self):
           return id(self)
    
    class UUT():
        def __init__(self, *args):
            resource_1 = ClassToPatch()
            resource_2 = ClassToPatch()
            self.test_property = (resource_1.some_func(), resource_2.some_func())
    
    class TestCase1(unittest.TestCase):
        @mock.patch('__main__.ClassToPatch', autospec = True)
        def test_1(self, mock1):
            ctpMocks = [mock.Mock(), mock.Mock()]
            ctpMocks[0].some_func.return_value = "funky"
            ctpMocks[1].some_func.return_value = "monkey"
            mock1.side_effect = ctpMocks
    
            u = UUT()
            self.assertEqual(u.test_property, ("funky", "monkey"))
    
    if __name__ == '__main__':
        unittest.main()
    

    I've added test_property to UUT so that the unit test does something useful. Now, without the mock test_property should be a tuple containing the ids of the two ClassToPatch instances. But with the mock it should be the tuple: ("funky", "monkey").

    I've used the side_effect property of the mock object so that a different instance of ClassToPatch is returned on each call in the UUT initialiser.

    Hope this helps.

    Edit: Oh, by the way, when I run the unit test I get:

    .
    ----------------------------------------------------------------------
    Ran 1 test in 0.004s
    
    OK