Декоратор работает для функции, но не для класса - PullRequest
0 голосов
/ 27 июня 2018

Нужна небольшая помощь в исправлении декоратора для unittest. Я не уверен, как выполнить требования модульного тестирования. Идея этого декоратора состоит в том, чтобы пометить тест как ожидаемый сбой, если передается значение True. В противном случае можно просто запустить тест. Этот декоратор работает для функций теста, но не работает для определений классов

import unittest

def expectedFailureIf(expFailure):
    if expFailure: 
        return unittest.expectedFailure
    return lambda func: func

@expectedFailureIf(GetCurrentOS() == kPlatMac)  # Fails on Class
class someClass(unittest.TestCase):
    #@expectedFailureIf(GetCurrentOS() == kPlatMac) # Works on Function
    def test_sometestA(self):
        assert True

    def test_sometestB(self):
        assert False

Ошибка, которую я получаю, test_sometest () принимает ровно 1 аргумент. Удаление декоратора позволяет запустить тест. Перемещение декоратора в начало функции позволяет запустить тест.

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

1 Ответ

0 голосов
/ 27 июня 2018

На Python 3 ваш код работает нормально. Реализация в unittest.expectedFailure лучше, и она работает правильно при декорировании классов или функций.

В Python 2 unittest.expectedFailure предназначен только для работы с функциями .

Вот замена, которая работает на Python 2.

import inspect
import types
import unittest

def expectedFailureIf(condition):
    if callable(condition):
        condition = condition()
    if not condition:
        # return identity function for no-op
        return lambda x: x
    def patch(func_or_class):
        if isinstance(func_or_class, types.FunctionType):
            return unittest.expectedFailure(func_or_class)
        for name, member in inspect.getmembers(func_or_class):
            if name.startswith('test') and isinstance(member, types.MethodType):
                setattr(func_or_class, name, unittest.expectedFailure(member))
        return func_or_class
    return patch

@expectedFailureIf(True)
class MyTest(unittest.TestCase):

    def test_that_passes(self):
        assert 2 + 2 == 4

    def test_that_fails(self):
        assert 2 + 2 == 5

if __name__ == "__main__":
    unittest.main(verbosity=2)

Результат:

test_that_fails (__main__.MyTest) ... expected failure
test_that_passes (__main__.MyTest) ... unexpected success

----------------------------------------------------------------------
Ran 2 tests in 0.001s

OK (expected failures=1, unexpected successes=1)

Внимание!

Тот факт, что неожиданный успех не провалит тестовый прогон, является ошибкой в ​​Python! Он был исправлен еще в январе 2014 года (Python 3.4), но это исправление не было объединено с веткой 2.7 из-за проблем с обратной совместимостью (см. Комментарии к issue20165 ). Итак, теперь это «особенность» Python 2 , к сожалению.

Если для вас это нарушает условия соглашения, рассмотрите возможность обновления до более последней версии Python и / или использования лучшего тестера .

...