Can I patch a Python decorator before it wraps a function?
Decorators are applied at function definition time. For most functions, this is when the module is loaded. (Functions that are defined in other functions have the decorator applied each time the enclosing function is called.)
So if you want to monkey-patch a decorator, what you need to do is:
- Import the module that contains it
- Define the mock decorator function
- Set e.g.
module.decorator = mymockdecorator
- Import the module(s) that use the decorator, or use it in your own module
If the module that contains the decorator also contains functions that use it, those are already decorated by the time you can see them, and you're probably S.O.L.
Edit to reflect changes to Python since I originally wrote this: If the decorator uses functools.wraps()
and the version of Python is new enough, you may be able to dig out the original function using the __wrapped__
attribute and re-decorate it, but this is by no means guaranteed, and the decorator you want to replace also may not be the only decorator applied.
Mock a decorator function to bypass decorator logic
Since the decorator runs immediately after you define get_value
, it's too late to mock the decorator. What you can do, though, (since you used functools.wraps
) is mock get_value
itself and use get_value.__wrapped__
(the original function) in some way. Something like
@patch('tmp.get_value', get_value.__wrapped__)
def test_something(self):
result = get_value(1)
self.assertEqual(1, result)
(In this case, I put your original code, with the above change, in tmp.py
, and ran it as python3 -munittest tmp.py
, hence my patching of the reference tmp.get_value
.)
If you anticipate the need to test the undecorated original, though, it might be simpler to keep it under its own (private) name to test: no patching needed.
import unittest
from functools import wraps
def decorator(f):
@wraps(f)
def decorated(x):
return f(x+1)
return decorated
def _get_value(x):
return x
get_value = decorator(_get_value)
class MyTestCase(unittest.TestCase):
def test_something(self):
result = _get_value(1)
self.assertEqual(1, result)
How to mock a decorated function
Python applies the decorator when loading the module so setting function_to_be_mocked
to mock_function
in test_decoratorated_mocked
would indeed change that function into an undecorated function.
You'd need to manually add the decorator again if you wish to mock function_to_be_mocked
:
mydecorator.function_to_be_mocked = mydecorator.my_decorator(mock_function)
How to remove the effects of a decorator while testing in python?
The retry
decorator you are using is built on top of the decorator.decorator
utility decorator with a simpler fallback if that package is not installed.
The result has a __wrapped__
attribute that gives you access to the original function:
orig = _sftp_command_with_retries.__wrapped__
If decorator
is not installed and you are using a Python version before 3.2, that attribute won't be present; you'd have to manually reach into the decorator closure:
orig = _sftp_command_with_retries.__closure__[1].cell_contents
(the closure at index 0 is the retry_decorator
produced when calling retry()
itself).
Note that decorator
is listed as a dependency in the retry
package metadata, and if you installed it with pip
the decorator
package would have been installed automatically.
You can support both possibilities with a try...except
:
try:
orig = _sftp_command_with_retries.__wrapped__
except AttributeError:
# decorator.decorator not available and not Python 3.2 or newer.
orig = _sftp_command_with_retries.__closure__[1].cell_contents
Note that you always can patch time.sleep()
with a mock. The decorator code will use the mock as it references the 'global' time
module in the module source code.
Alternatively, you could patch retry.api.__retry_internal
with:
import retry.api
def dontretry(f, *args, **kw):
return f()
with mock.patch.object(retry.api, '__retry_internal', dontretry):
# use your decorated method
This temporarily replaces the function that does the actual retrying with one that just calls your original function directly.
Python: decorator for mocking gettext during testing
I didn't find anything similar on the web, so I'm sharing it:
from unittest import mock
import functools
def mock_translation(tested_object):
"""
A decorator that mock the '_()' function during testing
just adds '_translated' after the string to translate
the class/function being tested needs to be passed as parameter
Use:
from my_module.sub_module import my_function_to_be_tested
@mock_translation(my_function_to_be_tested)
def test_my_function_to_be_tested():
pass
"""
def actual_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
module_name = tested_object.__module__
tested_obj = tested_object
breakpoint()
import_path = module_name + '._'
# with mock.patch('battletech.models._', lambda s: s+'_translated'):
with mock.patch(import_path, lambda s: s+'_translated'):
value = func(*args, **kwargs)
return value
return wrapper
return actual_decorator
pytest-monkeypatch a decorator (not using mock / patch)
It looks like the quick answer here is simply to move your Handler import so that it occurs after the patch. The decorator and the decorated functions must be in separate modules so that python doesn’t execute the decorator before you’ve patched it.
from decorators import user_login_required
@pytest.mark.parametrize('params', get_params, ids=get_ids)
def test_post(self, params, monkeypatch):
monkeypatch.setattr(decorators, "user_login_required" , mock_user_login_required_func)
from handlers.UserDetails import UserDetailsHandler
You may find this easier to accomplish using the patch function from the built in unittest.mock module.
Related Topics
How to Improve the Label Placement in Scatter Plot
Fastest Way to Take a Screenshot with Python on Windows
In Python What Is a Global Statement
How to Uninstall a Package Installed with Pip Install --User
Stop/Start/Pause in Python Matplotlib Animation
Compulsory Usage of If _Name_=="_Main_" in Windows While Using Multiprocessing
Executing Multiple Statements with Postgresql via SQLalchemy Does Not Persist Changes
Framerate Affect the Speed of the Game
What Can Multiprocessing and Dill Do Together
Convert Floating Point Number to a Certain Precision, and Then Copy to String
Zlib.Error: Error -3 While Decompressing: Incorrect Header Check
How to Use a Multiprocessing.Manager()
Why Aren't Superclass _Init_ Methods Automatically Invoked
Retrieving a Foreign Key Value with Django-Rest-Framework Serializers