Как можно избежать того, чтобы динамически сгенерированные классы, производные от метакласса, не превращались в один и тот же класс?

Я пытаюсь выполнить сотни модульных тестов для функции, которую я могу извлечь из словаря. К сожалению, я не могу использовать ни один из существующих пакетов для параметризованных тестов (например, носа), поэтому я пытаюсь найти собственное решение.

Мое намерение с помощью следующего примера кода состоит в том, чтобы создать 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 приводит к одному unittest.

1 ответ

Решение

Помимо глубокой попытки мета-всего, вы сталкиваетесь с типичной проблемой новичка в 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,

Другие вопросы по тегам