如何模拟单例 class 方法
How to mock a singleton class method
假设我们有以下结构:
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__
?
Class & 以双下划线开头的实例成员重写了它们的名称,以防止与父 class 中的同名成员发生冲突,使它们的行为与 "private" 相同。所以这里的 __B
实际上可以作为 A._A__B
访问。 (下划线,class 名称,双下划线成员名称)。请注意,如果您使用单下划线约定 (_B
),则不会发生重写。
话虽如此,您很少会看到有人实际使用这种形式的访问,并且 尤其是 不在产品代码中,因为 "private" 出于某种原因。如果没有更好的方法,也许是为了嘲笑。
我在 python 中嘲笑了很多东西,做了很多次之后我可以说:
- 从不 mock/patch
__something
属性(又称 私有 属性)
- 避免到mock/patch
_something
属性(又称受保护属性)
私人
如果你模拟私人事物,你会混淆生产和测试代码。当你做这种模拟时,总有一种方法可以通过修补或模拟 public 或受保护的东西来获得相同的行为。
为了更好地解释我所说的混淆生产代码和测试代码的意思,我可以使用您的示例:修补 A.__B.__to_be_mocked()
(我将 __A
内部 class 替换为 __B
为了更清楚)你需要写一些像
patch('amodule.A._A__B._B__to_be_mocked')
现在,通过修补 __to_be_mocked
,您正在测试中传播 A
、B
和 to_be_mocked
名称:这正是我所说的混乱代码。因此,如果您需要更改某个名称,您应该进行所有测试并更改您的补丁,并且没有重构工具可以建议您更改 _A__B._B
字符串。
现在,如果你是一个好人并且把你的测试搞得一清二楚,那么你可以只在几个点上出现这些名字,但如果它是一个单身人士,我敢打赌它会像蘑菇一样被发现。
我想指出 private 和 protected 与某些安全问题无关,它们只是让您的代码更清晰的一种方式。这一点在 python 中是 crystal 清楚的,你不需要成为黑客来更改私有或受保护的属性:这些约定在这里只是为了帮助你阅读代码,你可以说 哦太棒了!我不需要了解它是什么......它只是肮脏的工作。恕我直言,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()
方法?是否可以通过 patch/mock A
中的其他内容(而不是 A.__A
)产生相同的效果 class?
最后,如果您正在模拟私有方法来感知要测试的东西,那么您来错地方了:永远不要测试 脏工作 它 should/may/can 改变而不改变您的测试。您需要的是测试代码行为,而不是代码的编写方式。
受保护
如果你需要测试、补丁或模拟受保护的东西,也许你的 class 隐藏了一些合作者:测试它并使用你的测试来重构你的代码,然后清理你的测试。
免责声明
的确:我在我的测试中传播了这种废话,然后当我明白我可以做得更好时,我努力将其删除。
假设我们有以下结构:
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__
?
Class & 以双下划线开头的实例成员重写了它们的名称,以防止与父 class 中的同名成员发生冲突,使它们的行为与 "private" 相同。所以这里的 __B
实际上可以作为 A._A__B
访问。 (下划线,class 名称,双下划线成员名称)。请注意,如果您使用单下划线约定 (_B
),则不会发生重写。
话虽如此,您很少会看到有人实际使用这种形式的访问,并且 尤其是 不在产品代码中,因为 "private" 出于某种原因。如果没有更好的方法,也许是为了嘲笑。
我在 python 中嘲笑了很多东西,做了很多次之后我可以说:
- 从不 mock/patch
__something
属性(又称 私有 属性) - 避免到mock/patch
_something
属性(又称受保护属性)
私人
如果你模拟私人事物,你会混淆生产和测试代码。当你做这种模拟时,总有一种方法可以通过修补或模拟 public 或受保护的东西来获得相同的行为。
为了更好地解释我所说的混淆生产代码和测试代码的意思,我可以使用您的示例:修补 A.__B.__to_be_mocked()
(我将 __A
内部 class 替换为 __B
为了更清楚)你需要写一些像
patch('amodule.A._A__B._B__to_be_mocked')
现在,通过修补 __to_be_mocked
,您正在测试中传播 A
、B
和 to_be_mocked
名称:这正是我所说的混乱代码。因此,如果您需要更改某个名称,您应该进行所有测试并更改您的补丁,并且没有重构工具可以建议您更改 _A__B._B
字符串。
现在,如果你是一个好人并且把你的测试搞得一清二楚,那么你可以只在几个点上出现这些名字,但如果它是一个单身人士,我敢打赌它会像蘑菇一样被发现。
我想指出 private 和 protected 与某些安全问题无关,它们只是让您的代码更清晰的一种方式。这一点在 python 中是 crystal 清楚的,你不需要成为黑客来更改私有或受保护的属性:这些约定在这里只是为了帮助你阅读代码,你可以说 哦太棒了!我不需要了解它是什么......它只是肮脏的工作。恕我直言,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()
方法?是否可以通过 patch/mock A
中的其他内容(而不是 A.__A
)产生相同的效果 class?
最后,如果您正在模拟私有方法来感知要测试的东西,那么您来错地方了:永远不要测试 脏工作 它 should/may/can 改变而不改变您的测试。您需要的是测试代码行为,而不是代码的编写方式。
受保护
如果你需要测试、补丁或模拟受保护的东西,也许你的 class 隐藏了一些合作者:测试它并使用你的测试来重构你的代码,然后清理你的测试。
免责声明
的确:我在我的测试中传播了这种废话,然后当我明白我可以做得更好时,我努力将其删除。