【问题标题】:Mocking UUID in Python dataclasses在 Python 数据类中模拟 UUID
【发布时间】:2019-09-12 11:26:41
【问题描述】:

我有这样的 uuid 数据类:

import uuid

from dataclasses import dataclass, field
from typing import Union

@dataclass
class Foo:
    id: Union[uuid.UUID, None] = field(default_factory=uuid.uuid4)

当我调用 Foo() 时,它会创建一个带有生成的 UUID 的对象 - 很好。

现在我想在测试中模拟这个 UUID 工厂,如下所示:

from unittest.mock import patch

TEST_UUIDS = ["uuid1", "uuid2"]

with patch.object(uuid, "uuid4", side_effects=TEST_UUIDS):
    print(uuid.uuid4())  # Output: uuid1
    print(Foo().id)  # Output: an actual UUID

我的预期输出是uuid2。所以问题是:我如何正确修补工厂?在文档或此处找不到任何内容...

【问题讨论】:

    标签: python mocking python-dataclasses


    【解决方案1】:

    TL;DR 最好只做print(Foo(id="uuid1").id)

    说明

    以下是不起作用的:(除了 side_effect(s) 的错字)

    with patch.object(uuid, "uuid4", side_effect=TEST_UUIDS):
        print(id(uuid.uuid4))
        print(id(Foo.__dataclass_fields__["id"].default_factory))
    

    输出:

    140470453723216
    140470456506848
    

    这是因为 patch 并没有改变实际的 uuid.uuid4 函数,而是创建了一个新函数,覆盖了命名空间中的正常 uuid.uuid4,从 id 的变化可以看出。由于工厂已初始化,因此此更改无效,除非您在 with 块内创建类,这对于实际测试是不切实际的。

    但即使这样做了,也不会产生影响,因为dataclass 生成 __init__ 函数,并在during the class definition 处创建对默认工厂的引用,并且不是每个__init__ 电话。因此,以下也不起作用

    with patch.object(Foo.__dataclass_fields__["id"], "default_factory",
                      side_effect=TEST_UUIDS):
        print(Foo().id)
    

    您可以做的是覆盖__init__ 函数:

    from copy import copy
    #  Copy is required to avoid recursion after rewrite
    foo_init_copy = copy(Foo.__init__)
    def mock_init(foo):
        foo_init_copy(foo)
        foo.id = "x"
    
    with patch.object(Foo, "__init__", new=mock_init):
       ...
    

    如果工厂通常不实现复杂的逻辑,这对我来说似乎有点矫枉过正。如果是这样的话,也可以考虑创建一个普通类而不是数据类。

    【讨论】:

      猜你喜欢
      • 2021-03-13
      • 2015-04-10
      • 1970-01-01
      • 1970-01-01
      • 2019-01-11
      • 2019-01-09
      • 1970-01-01
      • 1970-01-01
      • 2018-12-25
      相关资源
      最近更新 更多