Skip to content

Instantly share code, notes, and snippets.

@abadger
Last active March 6, 2023 18:22
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save abadger/226de9d9d08eb329ca71bab010435a1a to your computer and use it in GitHub Desktop.
Save abadger/226de9d9d08eb329ca71bab010435a1a to your computer and use it in GitHub Desktop.
How to write a unittest that ignores a decorator
# let's say we have a function "testing" which is decorated by "my_decorator"
import functools
def my_decorator(func):
@functools.wraps(func)
def wrapped(*args, **kwargs):
print("In real decorator")
print(func.__name__)
print(args)
print(kwargs)
return func(*args, **kwargs)
# Python2 and Python3 < 3.2 compatibility
if not hasattr(wrapped, "__wrapped__"):
wrapped.__wrapped__ = func
return wrapped
@my_decorator
def testing(msg):
print("We got: %s" % msg)
# For some reason (maybe the decorator accesses resources which aren't
# available in the test environment), when we unittest, we want to test
# the function without its decoration.
import functools
try:
from unittest import mock
except:
import mock
import pytest
import decorate
# We want to replace the real decorator with one that doesn't do anything.
# In a real test, this decorator might do something like record how many
# times it was called.
def mock_decorator(func):
@functools.wraps(func)
def wrapped(*args, **kwargs):
print("In mock decorator")
return func(*args, **kwargs)
return wrapped
# This way does not work because the decorator is applied when "testing"
# Is defined. So monkeypatching the decorator at this point is too late.
# The Python interpreter has already defined "testing" when it imported
# "decorate" on line 14.
@pytest.fixture
def m_dec(monkeypatch):
monkeypatch.setattr(decorate, "my_decorator", mock_decorator)
def test_monkeypatching_decorator(capsys, m_dec):
print("In test function")
decorate.testing("Tested")
stdout = capsys.readouterr().out
assert "In real decorator" not in stdout
assert "In mock decorator" in stdout
# This way will work because it finds the original function (The undecorated "testing")
# and wraps it in our decorator that does nothing instead of the real decorator.
@pytest.fixture
def rewrap(monkeypatch):
original_func = decorate.testing.__wrapped__
print(id(decorate.testing))
print(original_func)
print(original_func.__name__)
print(id(original_func))
monkeypatch.setattr(decorate, "testing", mock_decorator(original_func))
print(id(decorate.testing))
def test_rewrap_decorated_function(capsys, rewrap):
decorate.testing("Tested")
stdout = capsys.readouterr().out
assert "In real decorator" not in stdout
assert "In mock decorator" in stdout
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment