如何模拟单例类方法

vks*_*vks 1 python unit-testing patch mocking python-2.7

假设我们有以下结构:

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)
Run Code Online (Sandbox Code Playgroud)

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

target = A.__A
target = A.___A
Run Code Online (Sandbox Code Playgroud)

还有很多。

编辑:

我解决了它使用

target=A._A__A and attribute as '_A__to_be_mocked`
Run Code Online (Sandbox Code Playgroud)

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

是因为setattributeinA还是__init__in A?

Mic*_*ico 5

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

  1. 永远不要模拟/补丁__something属性(又名私有属性)
  2. 避免模拟/修补_something属性(AKA受保护的属性)

私人的

如果你模拟私有的东西,你会纠结生产和测试代码。当你进行这种模拟时,总有一种方法可以通过修补或模拟公共或受保护的东西来获得相同的行为。

为了更好地解释我通过纠结生产和测试代码的意思,我可以使用您的示例:修补A.__B.__to_be_mocked() (我替换了__A内部类__B以使其更清晰)您需要编写类似

patch('amodule.A._A__B._B__to_be_mocked')
Run Code Online (Sandbox Code Playgroud)

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

现在,如果你是一个好人并且让你的测试干净利落,那么你可以在这些名字出现的地方只有几个点,但如果它是一个单身人士,我可以打赌它会像蘑菇一样被发现。

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

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

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

回到你的案例:你的代码如何使用__to_be_mocked()方法?是否可以通过补丁/模拟A(而不是A.__A)类中的其他内容来获得相同的效果?

最后,如果您正在模拟私有方法来感知要测试的内容,那么您就在错误的地方:永远不要测试它应该/可能/可以更改的脏工作,而无需更改您的测试。您需要的是测试代码行为,而不是它是如何编写的。

受保护

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

免责声明

确实:我在测试中传播了这种废话,然后当我知道我可以做得更好时,我会努力将其删除。