Манипулировать методами класса Fixture unittest через метакласс

Я хотел бы иметь метакласс для подклассов unittest.TestCase , что приведет к регистрации в журнале всех методов при их запуске и завершении, включая, например, @classmethods setUp и tearDown и их варианты.

К сожалению, фреймворк unittest очень требователен к именам методов, и их изменение приводит к тому, что фреймворк не находит их. Используя ответ на этот вопрос , у меня есть что-то вроде следующего:

  class _UTMeta(type):                  
      def __new__(cls, name, bases, dict_): 
          new_dict = {}              
          for fn_name, fn in dict_.items():                          
              if fn_name.startswith('test_'):                  
                  new_dict[fn_name] = lambda self: _UTMeta._wrapped_test_function(self, fn_name, fn)
                  new_dict[fn_name].__name__ = fn_name # (**)
              else:                        
                  new_dict[fn_name] = fn
          return type.__new__(cls, name, bases, new_dict)

      @staticmethod
      def _wrapped_test_function(self, name, fn):
          print 'starting', name
          _test_logger.info('starting ' + name)
          print 'finished', name

Обратите внимание на строку с комментарием # (**) — без него unittest не распознает методы как тестовые.

Это работает для обычных методов test_*, но не для classmethods. Чтобы проиллюстрировать проблему, я буду использовать classmethod setUpClass. Если я попытаюсь сделать то же самое, что и выше, то есть добавить

          if fn_name == 'setUpClass':                                      
              new_dict[fn_name] = lambda self: _UTMeta._wrapped_test_function(self, fn_name, fn)                            
              new_dict[fn_name].__name__ = fn_name

то я получаю:

ERROR: setUpClass (__main__._VersionDataTest)

TypeError: unbound method setUpClass() must be called with _VersionDataTest instance as first argument (got nothing instead)

Однако если я попытаюсь правильно добавить

          if fn_name == 'setUpClass':                                      
              new_dict[fn_name] = classmethod(lambda self: _UTMeta._wrapped_test_function(self, fn_name, fn))                            
              new_dict[fn_name].__name__ = fn_name

я получил

AttributeError: 'classmethod' object has no attribute '__name__'

Наконец, если я пропущу манипуляцию .__name__, unittest вообще не вызовет этот метод фиксации.

(Примечание этот вопрос задает что-то подобное, но принятый ответ предполагает, что эти методы не являются методами класса, и поэтому ИМХО совершенно неверен.)


person Ami Tavory    schedule 18.04.2016    source источник