4

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?

physicalattraction
  • 6,485
  • 10
  • 63
  • 122

2 Answers2

4

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())
wwii
  • 23,232
  • 7
  • 37
  • 77
  • Tank you, but I get the error that I mentioned. myFunction does not have uuid4 as an attribute. –  Mar 20 '19 at 19:43
1

It depends on your import. Let's say you have a module called module.py, and you have an import like this:

from uuid import uuid4

This means that in this module we now have a variable called uuid4. This is the thing to mock.

@patch('path.to.module.uuid4.int')
physicalattraction
  • 6,485
  • 10
  • 63
  • 122
  • I tried but then I'd get path.to.module is not a package. –  Mar 20 '19 at 19:05
  • Can you show your import statement then? Obviously, path.to.module is not literal, you should set your own path there. – physicalattraction Mar 20 '19 at 19:22
  • Yes, I have the path to the method there. @patch('utilities.user_data.myFunction.uuid1.int'), and the error says: No module named utilities.user_data.myFunction'. utilities.user_data is not a package. –  Mar 20 '19 at 19:32
  • A module is the Pythonic language for a file. Is the file that your function is in called `myFunction`? If so, what is your directory structure? – physicalattraction Mar 20 '19 at 20:08