[英]Weird behavior when using Singleton metaclass in Python unittest TestCase mixin
背景
我们有一个特殊的设置,我想为此编写一个 TestCase 基础 class / mixin,可用于为每个 TestCase 提供开箱即用的功能。 为 TestCase 提供功能的 class 需要是单个实例,因此实现为 singleton。
观察
制作 singleton 时,
这是我目前无法解释的一种非常奇怪的行为。
再生产
我确实测试了一个最小工作示例(MWE)来重现此问题,并且可以使用以下最少代码重现它。 请注意,我试图尽可能减少。 因此,该代码除了此复制之外没有任何其他目的,并且不遵循任何编码标准。
test_reproduction.py
import unittest
from unittest import TestCase
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
class SpecialMethods(metaclass=Singleton):
pass
class SpecialTestCase(TestCase, SpecialMethods):
def __init__(self, methodName="runTest") -> None:
TestCase.__init__(self, methodName)
SpecialMethods.__init__(self)
class TestSomething(SpecialTestCase):
def test_something1(self):
print('print test_something1')
self.assertTrue(True)
def test_something2(self):
print('print test_something2')
self.assertTrue(True)
def test_something3(self):
print('print test_something3')
self.assertTrue(True)
if __name__ == "__main__":
unittest.main()
使用以下命令执行时: python3 -m unittest -v test_reproduction
这会产生以下 output:
test_something1 (test_reproduction.TestSomething) ... print test_something1
ok
test_something1 (test_reproduction.TestSomething) ... print test_something1
ok
test_something1 (test_reproduction.TestSomething) ... print test_something1
ok
----------------------------------------------------------------------
Ran 3 tests in 0.000s
OK
请注意,只有 test_something1 被执行了 3 次。 test_something2 和 test_something3 没有被执行。
问题
有人对发生的事情有解释吗? 并且我可以通过 Mixin 使其工作的任何机会(我已经有其他解决方案,我只对通过 Mixin 使其工作感兴趣)。
您的设计有一些问题 - 但您得到了您的要求:您创建了一个洲际弹道导弹,并通过使用元类创建一个 singleton 来完成一个“单例”。 比,这就是你得到的。
我们通常不需要关心有多少实例或 unittest 对我们为运行所有测试而创建的测试类做了什么:但很明显,您修改了简单和常见的 class 行为,这只是让它中断。 换句话说: unittest 显然有内部机制来为它将运行的每个测试创建一个 class 的新实例,这不适用于您的安排。
与其试图弄清楚 unittest 的内部工作原理,以及它如何在每个实例中标记它正在运行的测试,不如离开你的烂摊子。
首先:单身人士被高估了。 用于创建 singleton 的元类被双重高估和错误。
第二:多个 inheritance 在非常特殊的情况下很好。 显然,想要将“必须是单例”的 class 与需要多个实例来运行测试的 class 结合起来,这不是其中一种情况。 改用合成。
所以,从:
class SpecialMethods: # <- that, just create the class
def my_special_method_1(self):
# do special things
...
SpecialMethods = SpecialMethods() # <- here is your singleton.
# no one is creating another instance of this by accident
class TestSomething(TestCase): # no even need for a intermediate class or a custom __init__
def test_something1(self):
print('print test_something1')
SpecialMethods.my_special_method1() # <surprise: this just works!!
self.assertTrue(True)
作为最后一个不完全相关的提示:也许您可以在测试中使用“pytest”而不是 unittest:测试需要更少的锅炉测试代码,包括不需要创建人工类来命名空间测试功能。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.