【问题标题】:How to mock uuid generation in a test case?如何在测试用例中模拟 uuid 生成?
【发布时间】:2017-11-07 19:25:58
【问题描述】:

我有一个测试用例,在该测试用例中,我创建了一个对象,我想在模块uuid 中模拟函数uuid4

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

我现在的问题是,当我运行编写两个测试用例时,第二次创建对象时,它会像第一次一样获取其他 uuid。因此,结果取决于我的测试类中测试用例的数量以及它们的运行顺序,这是我不想要的。

  • 这是模拟 uuid 生成器的最佳方式吗?
  • 如何在每次调用 setUp(或 tearDown)时重置或重新创建迭代器?

【问题讨论】:

    标签: python iterator mocking python-unittest


    【解决方案1】:

    答案比我想象的要容易:不要使用迭代器!相反,将 uuid 列表设置为模拟的 side_effect

    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
    

    编辑

    我找到了一种更好的方式来编写它作为上下文管理器,它允许根据上下文为 uuid 添加前缀。

    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
    

    解释:我正在使用patch.object(uuid, 'uuid4') 模拟函数uuid.uuid4。在其中,我将副作用定义为列表。如果你的副作用是一个列表,它可以看作是该函数在后续调用中的返回值列表,所以第一次调用函数uuid4(),它返回该列表的第一个元素,第二次第二个元素等。如果在上下文中我生成 10 个对象 A,则 UUID 将为 'obj_a_uuid_0''obj_a_uuid_9'

    【讨论】:

    • 更多关于side_effect的细节可以在这里找到:docs.python.org/3/library/…
    • 你能否扩展一下这个例子,如何使用它?我不明白为什么现在生成的uuid是一样的。以及为什么用字符串作为 uuid 前缀可以工作。谢谢
    • @Gonzalo:我已经为答案添加了更多解释。
    • 在您希望 real 键入 UUID 而不是字符串的情况下,[uuid.UUID(int=i) for i in range(0,10)] 之类的东西也应该可以工作。这在使用类型检查的较新版本的 python 中出现更多。
    • "不要使用迭代器!"为什么不呢?
    【解决方案2】:

    我找到了一个更好的解决方案:

    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
    

    这种方式是通用的,适用于所有边缘情况。

    【讨论】:

    • 如果您在测试中需要多个 uuid,这将如何工作?
    • 这不是和原来一样的缺点,如果你创建第二个测试用例在第一个之前运行,第一个测试用例突然得到不同的 uuid?
    猜你喜欢
    • 2013-12-04
    • 1970-01-01
    • 2023-03-08
    • 2019-12-07
    • 2023-01-18
    • 2019-06-09
    • 1970-01-01
    • 1970-01-01
    • 2019-08-04
    相关资源
    最近更新 更多