Python单元测试和自注册类

时间:2014-04-29 19:50:24

标签: python metaclass python-unittest

我刚刚将我的课程转换为自我注册"类。这是我第一次涉足python元类。奇怪的是,许多已经过去的测试开始以奇怪的方式失败。许多失败的测试将在自己运行时通过,而不是作为套件的一部分。这让我相信有某种"数据流失"测试之间。所以我创建了一些测试来确定这是否属实,我是对的。

现在的问题是,我该如何解决这个问题。使用del()的tearDown()方法不起作用。添加析构函数( del (self))不起作用。如何取消注册_registry中的项目,以便它们不会交叉污染其他测试?

有趣的是,下面的第一个测试通过,直到我添加第二个测试。

class IterateRegister(type):
    def __iter__(cls):
        return iter(cls._registry)

class TEST_IterateRegister_instantiation(unittest.TestCase):
    class foo(metaclass=IterateRegister):
        _registry = []

        def __init__(self, name):
            self._registry.append(self)
            self.name = name

    def test__iterate_register_metaclass(self):
        names = ['Shaggy', 'Scooby', 'Snack']

        x1 = TEST_IterateRegister_instantiation.foo(names[0])
        x2 = TEST_IterateRegister_instantiation.foo(names[1])
        x3 = TEST_IterateRegister_instantiation.foo(names[2])

        self.assertEqual(names, [ f.name for f in TEST_IterateRegister_instantiation.foo ])

    def test__destruction_deletes_class_from_registry(self):
        names = ['Shaggy', 'Scooby', 'Snack']

        x1 = TEST_IterateRegister_instantiation.foo(names[0])
        x2 = TEST_IterateRegister_instantiation.foo(names[1])
        x3 = TEST_IterateRegister_instantiation.foo(names[2])

        del(x2)
        names.remove('Scooby')

        self.assertEqual(names, [ f.name for f in TEST_IterateRegister_instantiation.foo ])

2 个答案:

答案 0 :(得分:1)

在这种情况下,我认为你不需要元类。

class foo(object):
    _registry = []

    def __init__(self, name):
        self._registry.append(self)
        self.name = name

为什么不直接迭代_registry?至于删除x2foo._registry的列表不亚于names;所以使用.remove方法。

def test__destruction_deletes_class_from_registry(self):
    names = ['Shaggy', 'Scooby', 'Snack']

    x1 = self.foo(names[0])
    x2 = self.foo(names[1])
    x3 = self.foo(names[2])

    foo._registry.remove(x2)
    names.remove('Scooby')

    self.assertEqual(names, [ f.name for f in self.foo._registry ])

答案 1 :(得分:1)

为什么明确使用类名而不是self?例如:

x1 = TEST_IterateRegister_instantiation.foo(names[0])
x2 = TEST_IterateRegister_instantiation.foo(names[1])
x3 = TEST_IterateRegister_instantiation.foo(names[2])

而不是:

x1 = self.foo(names[0])
x2 = self.foo(names[1])
x3 = self.foo(names[2])

此外,我不认为del正在按照您的想法行事。所有del都是remove a bound identifier from the namespace,所以:

del(x2)

相当于写x2 = None,这不是你想要做的。 Python没有析构函数。字符串是不可变的,对其进行更改不会更改names

解决您的问题,听起来您的问题是_registry是可变的,因此对cls._registry的更改对所有子类都是可见的。解决此问题的最简单方法是使用设置IterateRegister._registry = []的拆解方法。但是,我认为你应该重新考虑这个解决方案:如果它在你的测试中已经导致你IterateRegister持有状态的问题,那么跟踪它是不是很难在生产代码中?