Python объединяет — сбор нескольких декораторов @patch внутри другого декоратора

Я пишу внутреннюю структуру модульных тестов, которая включает в себя имитацию нескольких функций/классов.
@patch, похоже, отлично подходит для моих нужд, но поскольку есть много тестов, которые потребуют исправления многих разных классов/функций, я Я хочу избежать написания нескольких @patch перед каждым тестом и, возможно, инкапсулировать их все в другой декоратор. Чтобы лучше проиллюстрировать мои потребности:

Текущее состояние:

@patch('p.A', mockedA)
@patch('p.B', mockedB)
.
.
@patch('p.N', mockedN)
def test_this()

желаемое состояние:

@patch_all
def test_this()

Возможно ли реализовать что-то подобное? Пока мне это не удалось, так как @patch требует, чтобы следовал либо def, либо другой @.

EDIT 2:
Я попробовал предложение Микеле, но тест больше не определяется как тест:
После добавления functools.wraps в декоратор patch_all все заработало.

def patch_all(f):
@patch('p.A', moduleA.classA.methodA)
@patch('p.B', moduleB.classB.methodB)
.
.
@patch('p.N', moduleN.classN.methodN)
wraps(f)
def functor(*args, **kwargs):
    return f(*args, **kwargs)
return functor


class TestWrapper(unittest.TestCase):
    @patch_all
    def my_test(self):
        my test goes here...

С помощью декоратора @patch_all я получаю следующее:

nosetests Tester.py --nocapture 

----------------------------------------------------------------------
Ran 0 tests in 0.000s

OK

Если я удалю это:

$ nosetests Tester.py --nocapture 
.
----------------------------------------------------------------------
Ran 1 test in 7.692s

OK

Заранее спасибо.


person Meny Issakov    schedule 24.12.2014    source источник
comment
Имя вашего теста должно начинаться с test_, иначе бегун его не распознает.   -  person Michele d'Amico    schedule 28.12.2014
comment
Тоже не работает. Хотя, я считаю, что если бы это было необходимо, он бы не запустился, когда я удалил декоратор.   -  person Meny Issakov    schedule 28.12.2014
comment
Хорошо, это зависит от бегуна. Мне нужно время, чтобы посмотреть на это. Возможно, используйте декоратор @wrap, чтобы исправить это. Но я не могу попробовать это сейчас, я использую телефон. Я могу попробовать это позже в течение дня.   -  person Michele d'Amico    schedule 28.12.2014
comment
Ты прав! Я добавил functools.wraps, и он не работает.   -  person Meny Issakov    schedule 28.12.2014
comment
Я исправлю свой ответ позже   -  person Michele d'Amico    schedule 28.12.2014
comment
Я исправил это ... теперь вы можете проголосовать за него :)   -  person Michele d'Amico    schedule 28.12.2014


Ответы (1)


patch декоратор, как и все декораторы, — это просто функция, которая принимает функцию и возвращает функцию ([EDIT] в оригинальной версии я забыл @functools.wraps(f) сделать правильный тестовый декоратор, спасибо @MenyIssakov, чтобы сообщить мне что мой ответ был неверным). Вы можете определить свой собственный декоратор patch_all, например

def patch_all(f):
    @patch('p.A', argsA)
    @patch('p.B', argsB)
    .
    .
    @patch('p.N', argsN)
    @functools.wraps(f)
    def functor(*args, **kwargs):
        return f(*args, **kwargs)
    return functor

Теперь вы можете использовать декоратор @patch_all в своих тестах, например:

@patch_all
def test_all(mockN, ..., mockB, mockA):
    my beautiful test

Вы можете перейти к этому и определить свой собственный декоратор, который берет список кортежей для передачи в вызовы исправлений.

Однако я думаю, что это не очень хорошая идея: тест должен быть простым, а насмешка должна быть явной, чтобы прояснить цель теста. Если вам нужно исправить множество объектов/методов/функций во многих функциях тестов, рассмотрите возможность примените декоратор к классу вместо отдельных методов, чтобы написать его только один раз для всех тестовых методов.

person Michele d'Amico    schedule 25.12.2014
comment
Привет, Мишель, я пробовал твой пример, и раньше делал что-то подобное. моя проблема после использования этого метода заключается в том, что тест больше не идентифицируется как модульный тест (я использую нос). После того, как я удалю аннотацию patch_all и запущу тесты socks.py, он попытается запустить мой тест, если я верну аннотацию patch_all, я получаю сообщение о том, что он выполнил 0 тестов. - person Meny Issakov; 28.12.2014