Skip to main content
 首页 » 编程设计

python之如何模拟单例类方法

2025年05月04日35langtianya

假设我们有以下结构:

class A(): 
    class __A(): 
        def __to_be_mocked(self): 
            #something here 
    def __init__(self): 
        with A.lock: 
            if not A.instance: 
                A.instance = A.__A() 
 
    def __getattr__(self,name): 
        return getattr(self.instance,name) 

现在我们要模拟函数__to_be_mocked。我们如何模拟它作为mock.patch.object接受的目标是package.module.ClassName 。我已经尝试了所有方法,例如

target = A.__A 
target = A.___A 

还有更多。

编辑:

我用

解决了它
target=A._A__A and attribute as '_A__to_be_mocked` 

现在的问题是 __to_be_mocked 在 __A 里面,所以它不应该是 ___A__to_be_mocked 吗。

是因为A中的setattribute还是A中的__init__?

请您参考如下方法:

我在 python 中模拟了很多东西,在做了很多次之后我可以说:

  1. 从不 模拟/修补 __something 属性(又称 private 属性)
  2. 避免模拟/修补_something 属性(又名protected 属性)

私有(private)

如果您模拟私有(private)事物,您会混淆生产和测试代码。当您进行此类模拟时,总有一种方法可以通过修补或模拟公共(public)或 protected 内容来获得相同的行为。

为了更好地解释我所说的混淆生产代码和测试代码的意思,我可以使用您的示例:修补 A.__B.__to_be_mocked() (我替换了 __A 内部类通过 __B 使其更清楚)你需要写一些像

patch('amodule.A._A__B._B__to_be_mocked') 

现在,通过修补 __to_be_mocked,您可以在测试中传播 A、B 和 to_be_mocked 名称:即正是我所说的纠结代码。因此,如果您需要更改某个名称,您应该进入所有测试并更改您的补丁,并且没有任何重构工具可以建议您更改 _A__B._B 字符串。

现在,如果你是一个好人并且把你的测试搞得一清二楚,那么你可以只在几个点上出现这些名字,但如果它是一个单例,我敢打赌它会像蘑菇一样被发现。

我想指出 private 和 protected 与某些安全问题无关,它们只是让您的代码更清晰的一种方式。这一点在 Python 中非常清楚,您无需成为黑客即可更改私有(private)或 protected 属性:这些约定在这里只是为了帮助您阅读可以说 Oh great 的代码!我不需要了解它是什么......它只是肮脏的工作。恕我直言,python 中的私有(private)属性无法实现此目标(__ 太长,看到它真的很困扰我)并且 protected 就足够了。

旁注:理解 python 私有(private)命名的小例子:

>>> class A(): 
...  class __B(): 
...   def __c(self): 
...    pass 
...  
>>> a = A() 
>>> dir(a) 
['_A__B', '__doc__', '__module__'] 
>>> dir(a._A__B) 
['_B__c', '__doc__', '__module__'] 

回到你的案例:你的代码如何使用 __to_be_mocked() 方法?是否可以通过在 A(而不是 A.__A)类中修补/模拟其他东西来产生相同的效果?

最后,如果您正在模拟私有(private)方法来感知要测试的东西,那么您来错地方了:永远不要测试肮脏的工作,它应该/可能/可以在不改变您的测试的情况下改变。您需要的是测试代码行为,而不是代码的编写方式。

protected

如果你需要测试、补丁或模拟 protected 东西,也许你的类隐藏了一些合作者:测试它并使用你的测试来重构你的代码,然后清理你的测试。

免责声明

的确:我在我的测试中散布了这种废话,然后当我明白我可以做得更好时,我会努力将其删除。