Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to mock uuid generation in a test case?

I have a test case in which in the setUp, I create an object for which I want to mock the function uuid4 in the module uuid.

TEST_UUIDS = ['uuid_{}'.format(i) for i in range(10000)]
UUID_POOL = iter(TEST_UUIDS)

def generate_uuid() -> str:
    return next(UUID_POOL)

class MyTestCase(TestCase):

    def setUp(self, **kwargs):
        self._create_my_object

    @patch.object(uuid, 'uuid4', generate_uuid)
    def _create_my_object(self):
        # Create an object using a uuid

My problem now is that when I run write two test cases, the second time the object is created, it gets other uuids as the first time. Therefore, the result is dependent on the number of test cases in my test class and the order they are run in, something I don't want.

  • Is this the best way to mock a uuid generator?
  • How do I reset or recreate the iterator at every call of setUp (or tearDown)?
like image 940
physicalattraction Avatar asked Jun 06 '17 16:06

physicalattraction


People also ask

How do you generate a UUID from a string in Python?

uuid1() function is used to generate a UUID from the host ID, sequence number, and the current time. It uses the MAC address of a host as a source of uniqueness. The node and clock_seq are optional arguments. The node is the hardware address, which is a 48-bit positive integer.

What is UUID generator?

A UUID (Universal Unique Identifier) is a 128-bit value used to uniquely identify an object or entity on the internet. Depending on the specific mechanisms used, a UUID is either guaranteed to be different or is, at least, extremely likely to be different from any other UUID generated until A.D. 3400.

How do you generate multiple UUIDs in Python?

This module provides immutable UUID objects (the UUID class) and the functions uuid1() , uuid3() , uuid4() , uuid5() for generating version 1, 3, 4, and 5 UUIDs as specified in RFC 4122. If all you want is a unique ID, you should probably call uuid1() or uuid4() .


2 Answers

The answer was easier than I thought: just don't use iterators! Instead, set the list of uuids as side_effect of the mock.

TEST_UUIDS = ['uuid_{}'.format(i) for i in range(10000)]

class MyTestCase(TestCase):

    def setUp(self, **kwargs):
        self._create_my_object

    @patch.object(uuid, 'uuid4', side_effect=TEST_UUIDS)
    def _create_my_object(self):
        # Create an object using a uuid

EDIT

I found an even nicer way to write this as a context manager, which allows for prefixing uuids based on the context.

TEST_UUIDS = ['uuid_{}'.format(i) for i in range(10000)]

def uuid_prefix(prefix: str):
    return patch.object(uuid, 'uuid4', side_effect=['{}_{}'.format(prefix, x) for x in TEST_UUIDS])

class MyTestCase(TestCase):

    def setUp(self, **kwargs):
        self._create_my_object

    def _create_my_object(self):
        with uuid_prefix('obj_a'):
            # Create an object A using a uuid
        with uuid_prefix('obj_b'):
            # Create an object B using a uuid

Explanation: I am mocking the function uuid.uuid4 using patch.object(uuid, 'uuid4'). In it, I define a side effect as a list. If your side effect is a list, it can be seen as a list of return values of that function on subsequent calls, so the first time the function uuid4() is called, it returns the first element of that list, the second time the second element, etc. If in the with-context I am generating 10 objects A, the UUIDs will be 'obj_a_uuid_0' up to 'obj_a_uuid_9'.

like image 151
physicalattraction Avatar answered Oct 20 '22 02:10

physicalattraction


I found an even nicer solution:

TEST_UUIDS_COUNT = 0
def mock_uuid():
    global TEST_UUIDS_COUNT
    TEST_UUIDS_COUNT += 1
    return uuid.UUID(int=TEST_UUIDS_COUNT)

class MyTestCase(TestCase):

    def setUp(self, **kwargs):
        self._create_my_object

    @patch('uuid.uuid4', mock_uuid)
    def _create_my_object(self):
        # Create an object using a uuid

This way it is universal and works with all edge cases.

like image 2
Mindaugas Jaraminas Avatar answered Oct 20 '22 02:10

Mindaugas Jaraminas