如何模拟单例类方法

时间:2016-02-23 05:13:28

标签: python python-2.7 unit-testing mocking patch

假设我们有以下结构:

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

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

2 个答案:

答案 0 :(得分:3)

我在python中嘲笑了很多东西,经过很多次我可以说:

  1. 从不模拟/补丁__something属性(AKA 私有属性)
  2. 避免模拟/修补_something属性(AKA 受保护的属性)
  3. 私人

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

    为了更好地解释拼写生产和测试代码的含义,我可以使用您的示例:修补A.__B.__to_be_mocked()(我将__A内部类替换为__B以使其更清晰)你需要写一些像

    这样的东西
    patch('amodule.A._A__B._B__to_be_mocked')
    

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

    现在,如果你是一个好人,并且你的测试很干净,那么这些名字就会出现几个点,但如果是单身,我可以打赌它会像蘑菇一样发现。

    我想指出私有和受保护与某些安全问题无关,但只是让您的代码更清晰的方法。这一点在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__']
    

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

    最后,如果你正在嘲笑私有方法来感知要测试的东西你在错误的地方:永远不要测试脏工作它应该/可能/可以在不改变测试的情况下改变。您需要的是测试代码行为而不是如何编写代码行为。

    受保护的

    如果你需要测试,补丁或模拟保护的东西,你的班级可能会隐藏一些协作者:测试它并使用你的测试来重构你的代码然后清理你的测试。

    声明

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

答案 1 :(得分:1)

Class&amp;以双下划线开头的实例成员的名称被重写,以防止与父类中的同名成员发生冲突,使它们表现得好像&#34; private&#34;。因此__B实际上可以A._A__B访问_B。 (下划线,类名,双下划线成员名称)。请注意,如果使用单下划线约定(SharedQueue<T>),则不会进行重写。

话虽这么说,你很少会看到任何人实际使用这种形式的访问权限,并且尤其是而不是生产代码,因为事情已经制定出来了#34;私人&#34;因为某种原因。对于嘲笑,也许,如果没有更好的方法。