unittest и metaclass: автоматическая генерация метода test_ * - PullRequest
4 голосов
/ 03 марта 2011

Когда я создаю тесты для фреймворка, я начинаю замечать следующий шаблон:

class SomeTestCase(unittest.TestCase):

    def test_feat_true(self):
        _test_feat(self, True)

    def test_feat_false(self):
        _test_feat(self, False)

    def _test_feat(self, arg):
        pass    # test logic goes here

Итак, я хочу программно создать test_feat_* методы для этих типов тестовых классов с метаклассом. Другими словами, для каждого частного метода с подписью _test_{featname}(self, arg) я хочу создать два обнаруживаемых метода верхнего уровня с сигнатурами test_{featname}_true(self) и test_{featname}_false(self).

Я придумал что-то вроде:

#!/usr/bin/env python

import unittest


class TestMaker(type):

    def __new__(cls, name, bases, attrs):
        callables = dict([
            (meth_name, meth) for (meth_name, meth) in attrs.items() if
            meth_name.startswith('_test')
        ])

        for meth_name, meth in callables.items():
            assert callable(meth)
            _, _, testname = meth_name.partition('_test')

            # inject methods: test{testname}_{[false,true]}(self)
            for suffix, arg in (('false', False), ('true', True)):
                testable_name = 'test{0}{1}'.format(testname, suffix)
                attrs[testable_name] = lambda self: meth(self, arg)

        return type.__new__(cls, name, bases, attrs)


class TestCase(unittest.TestCase):

    __metaclass__ = TestMaker

    def _test_this(self, arg):
        print 'this: ' + str(arg)

    def _test_that(self, arg):
        print 'that: ' + str(arg)


if __name__ == '__main__':
    unittest.main()

Я ожидаю некоторый вывод вроде:

this: False
this: True
that: False
that: True

Но я получил:

$ ./test_meta.py
that: True
.that: True
.that: True
.that: True
.
----------------------------------------------------------------------
Ran 4 tests in 0.000s

OK

Похоже, есть некоторые правила закрытия, которые я пропускаю. Как мне обойти это? Есть ли лучший подход?

Спасибо

edit : исправлено. См .: Фрагмент .

Ответы [ 2 ]

5 голосов
/ 03 марта 2011

Действительно, это проблема закрытия:

Изменить

attrs[testable_name] = lambda self: meth(self, arg)

на

attrs[testable_name] = lambda self,meth=meth,arg=arg: meth(self, arg)

Используя значение по умолчанию, arg внутри лямбдыпривязан к значению по умолчанию arg, установленному во время каждой итерации цикла.Без значения по умолчанию arg принимает последнее значение arg после завершения всех итераций цикла.(И то же самое относится к meth).

1 голос
/ 03 марта 2011

Вместо того, чтобы идти по маршруту метакласса, я бы рассмотрел использование генераторов носового теста для такого рода вещей:

http://somethingaboutorange.com/mrl/projects/nose/1.0.0/writing_tests.html#test-generators

Недостатком тестовых генераторов является то, что они являются специфической особенностью носа, поэтому вам необходимо ввести зависимость вне stdlib. Плюс в том, что я думаю, что их легче писать и понимать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...