Следует отметить, что некоторые из ответов здесь будут исправлять декоратор для всего сеанса тестирования, а не для одного экземпляра теста; что может быть нежелательно. Вот как можно установить исправление для декоратора, который сохраняется только в течение одного теста.
Наш блок будет протестирован с нежелательным декоратором:
# app/uut.py
from app.decorators import func_decor
@func_decor
def unit_to_be_tested():
# Do stuff
pass
Из модуля декораторов:
# app/decorators.py
def func_decor(func):
def inner(*args, **kwargs):
print "Do stuff we don't want in our test"
return func(*args, **kwargs)
return inner
К тому времени, когда наш тест будет собран во время тестового прогона, нежелательный декоратор уже был применен к нашему тестируемому устройству (потому что это происходит во время импорта). Чтобы избавиться от этого, нам нужно вручную заменить декоратор в модуле декоратора, а затем повторно импортировать модуль, содержащий наше проверяемое оборудование.
Наш тестовый модуль:
# test_uut.py
from unittest import TestCase
from app import uut # Module with our thing to test
from app import decorators # Module with the decorator we need to replace
import imp # Library to help us reload our UUT module
from mock import patch
class TestUUT(TestCase):
def setUp(self):
# Do cleanup first so it is ready if an exception is raised
def kill_patches(): # Create a cleanup callback that undoes our patches
patch.stopall() # Stops all patches started with start()
imp.reload(uut) # Reload our UUT module which restores the original decorator
self.addCleanup(kill_patches) # We want to make sure this is run so we do this in addCleanup instead of tearDown
# Now patch the decorator where the decorator is being imported from
patch('app.decorators.func_decor', lambda x: x).start() # The lambda makes our decorator into a pass-thru. Also, don't forget to call start()
# HINT: if you're patching a decor with params use something like:
# lambda *x, **y: lambda f: f
imp.reload(uut) # Reloads the uut.py module which applies our patched decorator
Обратный вызов очистки, kill_patches, восстанавливает исходный декоратор и повторно применяет его к тестируемому модулю. Таким образом, наш патч сохраняется только через один тест, а не весь сеанс - именно так должен вести себя любой другой патч. Кроме того, поскольку очистка вызывает patch.stopall (), мы можем запускать любые другие исправления в setUp (), которые нам нужны, и они будут очищены все в одном месте.
Важная вещь, которую нужно понять об этом методе, это то, как перезагрузка повлияет на вещи. Если модуль занимает слишком много времени или имеет логику, которая запускается при импорте, вам, возможно, нужно просто пожать плечами и протестировать декоратор как часть устройства. :( Надеюсь, ваш код написан лучше, чем это. Верно?
Если вам все равно, будет ли исправление применено ко всему сеансу теста , самый простой способ сделать это - прямо вверху файла теста:
# test_uut.py
from mock import patch
patch('app.decorators.func_decor', lambda x: x).start() # MUST BE BEFORE THE UUT GETS IMPORTED ANYWHERE!
from app import uut
Убедитесь, что патч файла выполняется с помощью декоратора, а не локальной области проверяемого оборудования, и запустите патч перед импортом устройства с декоратором.
Интересно, что даже если патч остановлен, на все файлы, которые уже импортированы, все равно будет наложен патч на декоратор, что является противоположностью ситуации, с которой мы начали. Имейте в виду, что этот метод будет исправлять любые другие файлы в тестовом прогоне, которые впоследствии импортируются, даже если они сами не объявляют исправление.