20

For testing reasons, I need to be able to mock the inner/original function of a decorated one which is used somewhere else:

In mydecorator.py:

def my_decorator(f):
    def wrapped_f():
        print "decorated"
        f()
    return wrapped_f


@my_decorator
def function_to_be_mocked():
    print 'original'


def function_to_be_mocked_undecorated():
    print 'original'


def run_decorated():
    function_to_be_mocked()


def run_undecorated():
    decorated_funtion = my_decorator(function_to_be_mocked_undecorated)
    decorated_funtion()

As you can see, I have several versions of the original function function_to_be_mocked, one with the decorator my_decorator and one 'naked'. The runner function run_decorated() calls the decorated version of function_to_be_mocked and run_undecorated() calls the undecorated version and applies the decorator 'manually'. The result of both are the same:

decorated
original

Now I want to test the runner function but I need to mock the original function function_to_be_mocked but also the mocked version should be decorated:

import unittest
import mydecorator
from mock import patch

def mock_function():
    print 'mockified'

class Test(unittest.TestCase):

    @patch('mydecorator.function_to_be_mocked_undecorated')
    def test_undecorated_mocked(self, mock_function_to_be_mocked_undecorated):
        mydecorator.function_to_be_mocked_undecorated = mock_function
        mydecorator.run_undecorated()
        assert 1==0

    @patch('mydecorator.function_to_be_mocked')
    def test_decoratorated_mocked(self, mock_function_to_be_mocked):
        mydecorator.function_to_be_mocked = mock_function
        mydecorator.run_decorated()
        assert 1==0

This works as expected for the undecorated version test_undecorated_mocked:

decorated
mockified

But the decorated version gives:

mockified

so the decorator vanished.

Is it possible to get the decorated version working in the same way as the undecorated version, where the decorator is applied 'manually'?

I tried to expose the inner function in the decorator without success.

I saw this question How do you mock a function which has decorator apply to it in a unit test? but this doesn't help me.

Community
  • 1
  • 1
SebastianNeubauer
  • 543
  • 1
  • 5
  • 9

1 Answers1

19

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)
Simeon Visser
  • 118,920
  • 18
  • 185
  • 180
  • Wow, absolutely amazing, this short answer solves my problem!! – SebastianNeubauer Nov 06 '13 at 13:22
  • 1
    I can absolutely live with that, but am I completely wrong to look at this and feel like it is kind of a workaround. Is this just a not-so-nice corner of python or is my design just not python like (monkey patching a decorated function)? – SebastianNeubauer Nov 06 '13 at 13:28
  • 1
    @user27564: I think so yes; this is how decorators are applied in Python so when you mock you have to apply them again. Python applies a decorator and replaces the original function so when you're mocking you need to apply the decorator again, Python doesn't keep track of that. The mocking library allows you to mock but it does mean manual work is needed for decorators. – Simeon Visser Nov 06 '13 at 13:36