Search code examples
pythonpython-3.xmockingpython-mock

How to mock modules python, patch does not find the attribute


In a function I'm using uuid1 that I want to patch.

def myFunction():
     my_value = uuid4.int
     smth else..

I want to be able to mock my_value so it always returns the same number in my unit test, because I need it for further use. I tried doing:

 @patch('folder.myFunction.uuid4')
 def test_myFunction(self, mock_value):
      mock_value.return_value = 22222

But it throws an error saying myFunction does not have uuid4 as an attribute.

How do I mock its value?


Solution

  • The error you get is correct. Your function does not have a uuid4 attribute.

    I'm reading between the lines assuming uuid4 is a method of the uuid module that normally generates a random uuid.

    When testing you said you want it to always return the same value. To do that you can substitute a unittest.mock.Mock for uuid.uuid4.

    In [36]: uuid_mock = Mock(return_value=uuid.UUID('77f1df52-4b43-11e9-910f-b8ca3a9b9f3e'))
    
    In [37]: uuid_mock()
    Out[37]: UUID('77f1df52-4b43-11e9-910f-b8ca3a9b9f3e')
    

    Something like this for testing the following function (f)

    import uuid, unittest
    from unittest.mock import Mock, patch
    
    def f():
        z = uuid.uuid4()
        return z.int
    

    The target for the patch is the uuid method - uuid.uuid4. Specify a unittest.mock.Mock with a fixed return value for the new parameter of the patch. During the test, the Mock will be substituted for uuid.uuid4

    class TestF(unittest.TestCase):
    
        uuid_mock = Mock(return_value=uuid.UUID('77f1df52-4b43-11e9-910f-b8ca3a9b9f3e'))
    
        good_uuid = uuid.UUID('77f1df52-4b43-11e9-910f-b8ca3a9b9f3e').int
        bad_uuid = uuid.UUID('77f1df52-4b43-11e9-910f-b8ca3a9b5a31').int
    
        @patch(target='uuid.uuid4', new=TestF.uuid_mock)
        def test_myFunction_True(self):
            self.assertEqual(f(), self.good_uuid)
    
        @patch(target='uuid.uuid4', new=TestF.uuid_mock)
        def test_myFunction_False(self):
            self.assertNotEqual(f(), self.bad_uuid)
    
    if __name__ == '__main__':
        unittest.main()
    

    Result:

    ..
    ----------------------------------------------------------------------
    Ran 2 tests in 0.001s
    
    OK
    

    If you want to test a function that relies on f's return value and you want f to always return the same value during testing then make f the target for the patch.

    def g():
        return f() == uuid.UUID('77f1df52-4b43-11e9-910f-b8ca3a9b9f3e').int
    
    class TestG(unittest.TestCase):
        good_uuid_mock = Mock(return_value=uuid.UUID('77f1df52-4b43-11e9-910f-b8ca3a9b9f3e').int)
        bad_uuid_mock = Mock(return_value=uuid.UUID('77f1df52-4b43-11e9-910f-b8ca3a9b5a31').int)
    
        @patch(target='__main__.f', new=TestG.good_uuid_mock)
        def test_myFunction_True(self):
            self.assertTrue(g())
        @patch(target='__main__.f', new=TestG.bad_uuid_mock)
        def test_myFunction_False(self):
            self.assertFalse(g())