繁体   English   中英

在 Python unittest TestCase mixin 中使用 Singleton 元类时的奇怪行为

[英]Weird behavior when using Singleton metaclass in Python unittest TestCase mixin

背景

我们有一个特殊的设置,我想为此编写一个 TestCase 基础 class / mixin,可用于为每个 TestCase 提供开箱即用的功能。 为 TestCase 提供功能的 class 需要是单个实例,因此实现为 singleton。


观察

制作 singleton 时,

  • 测试发现不再正常工作,仅检测到 TestCase 的单个测试
  • 并且单个发现的测试被执行 N 次,其中 N 是 TestCase 中的测试数。

这是我目前无法解释的一种非常奇怪的行为。


再生产

我确实测试了一个最小工作示例(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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM