如何避免从元类派生的动态生成的类不会最终成为同一个类?

时间:2015-03-25 03:11:57

标签: python unit-testing metaclass

我尝试做的是对一个函数执行数百个单元测试,我可以从字典中派生出来。不幸的是,我不能使用任何现有的包进行参数化测试(比如鼻子),所以我试图找到自己的解决方案。

我对以下示例代码的意图是创建3个类(每个测试一个),这些类将存在于全局范围内,以便单元测试可以拾取它并运行相应的测试。

tests = [
    {'text': 'text1fdskla3fsda4',
     'result': [1, 3, 4],
     },
    {'text': 'fdsg45tg5b',
     'result': [4, 5, 5,5 ],
     },
    {'text': 'fsddf4',
     'result': [4, 2],
   }
]

def evaluate(text):
    out = []
    for char in text:
        if char.isdigit():
            out.append(int(char))
    return out

class TestMeta(type):
    def __new__(cls, name, bases, attrs):
        name = str(test['text'])
        return type.__new__(cls, name, (unittest.TestCase,), attrs)

for test in tests:
    class T(object):
        __metaclass__ = TestMeta
        def testOne(self):
            self.assertEqual(test['result'], evaluate(test['text']))
    globals()[(test['text'])] = copy.deepcopy(T)

unittest.main()

当我运行上面的代码时,我得到了四个单元测试,这比预期的多一个,但最重要的是,不是每个单元测试的输出,而是我创建的类似乎总是相同(即使我实际上为每个设置了不同的名称和参数):

======================================================================
FAIL: testOne (__main__.fsddf4)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "ble.py", line 45, in testOne
    self.assertEqual(test['result'], evaluate(test['text']))
AssertionError: [4, 2] != [4]

======================================================================
FAIL: testOne (__main__.fdsg45tg5b)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "ble.py", line 45, in testOne
    self.assertEqual(test['result'], evaluate(test['text']))
AssertionError: [4, 2] != [4]

======================================================================
FAIL: testOne (__main__.fsddf4)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "ble.py", line 45, in testOne
    self.assertEqual(test['result'], evaluate(test['text']))
AssertionError: [4, 2] != [4]

======================================================================
FAIL: testOne (__main__.text1fdskla3fsda4)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "ble.py", line 45, in testOne
    self.assertEqual(test['result'], evaluate(test['text']))
AssertionError: [4, 2] != [4]

----------------------------------------------------------------------
Ran 4 tests in 0.002s

FAILED (failures=4)

deepcopy是尝试获得不同的输出,但没有帮助。在globals dict中显式创建类是因为简单地在for循环上创建类只产生一个单元测试。

1 个答案:

答案 0 :(得分:1)

除了对meta-everything的深入尝试之外,你还遇到了一个典型的Python初学者问题:全局名称查找是在运行时完成的。

所以,在你的代码中:

for test in tests:
    class T(object):
        __metaclass__ = TestMeta
        def testOne(self):
            self.assertEqual(test['result'], evaluate(test['text']))

testOne运行时,它会在test词典中查找globals - 运行的时间 - 当然,test设置为最近设置的值。

您需要强制更早发生绑定,您可以这样做,只需更改

即可
        def testOne(self):

        def testOne(self, test=test):

此更改会强制在test执行时查找全局def(与方法执行正文的后期相反)是class语句执行的同一时间 - 即循环的每一段,当全局变量test设置为列表tests的当前项时。