【问题标题】:How to mock a singleton class method如何模拟单例类方法
【发布时间】:2016-02-23 05:13:28
【问题描述】:

假设我们有以下结构:

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 python-2.7 unit-testing mocking patch


    【解决方案1】:

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

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

    私人

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

    为了更好地解释我通过纠结生产和测试代码的意思,我可以使用您的示例:修补 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)类中修补/模拟其他内容来获得相同的效果?

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

    受保护

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

    免责声明

    确实:我在测试中散布这种废话,然后当我知道我可以做得更好时,我会努力消除它。

    【讨论】:

    • 我已经mock成功了....唯一剩下的理解是d name mangling是如何工作的
    • 我现在读了...很久以前,当我停止使用私有属性时,我停止进入私有命名。恕我直言,他们只是从 Java 单词中制造噪音,_ 一个就足够了。我写答案不是为了涵盖技术部分,而是为了分享我做这类事情的经验。
    • @Micheled'Amico 很好的答案。我也以此为生。 :) +1
    • 你能分享为什么不能模拟私有属性......任何实例或例子吗?
    • @vks 我添加了一些解释。我希望这已经足够了,但是在那里我应该开始谈论 python 中的私有属性和受保护属性,并解释我的测试目标是主要重构......这意味着它可能是一本书的章节,我不能写它.
    【解决方案2】:

    以双下划线开头的类和实例成员的名称被重写,以防止与父类中的同名成员发生冲突,使它们表现得好像“私有”。所以这里的__B 实际上可以作为A._A__B 访问。 (下划线、类名、双下划线成员名)。请注意,如果您使用单下划线约定 (_B),则不会发生重写。

    话虽如此,您很少会看到有人真正使用这种访​​问形式,尤其是不在产品代码中,因为某些东西被“私有化”是有原因的。如果没有更好的方法,也许是为了嘲笑。

    【讨论】:

    • thanx 很多.........我用的是A._A.__B,但应该是A._A__B。不知何故错过了它。但现在我有另一个问题。你能检查一下更新
    • 重写只适用于__开头的属性。所以不应该是A._A__A.to_be_mocked
    • 函数to_be_mocked实际上是__to_be_mocked.updated question。所以我的问题是关于那个
    • 哦,我明白了,你问为什么它仍然是_A__to_be_mocked。不确定,可能 Python 只是忽略了额外的前导下划线来进行名称修改。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-04
    • 2015-03-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多