Jerry
Jerry

Reputation: 685

Mock a decorator function to bypass decorator logic

I'm trying write some unittests for my code which in turn uses a decorator

import unittest
from unittest.mock import patch
from functools import wraps

def decorator(f):
    @wraps(f)
    def decorated(x):
        return f(x+1)
    return decorated

@decorator
def get_value(x):
    return x
    
class MyTestCase(unittest.TestCase):
    @patch('file.decorator', lambda f: f)
    def test_something(self):
        result = get_value(1)
        self.assertEqual(1, result)

I'm trying to mock the decorated function to just return f and completely bypass the decorator logic part. This is talked about in Overriding decorator during unit test in python but doesn't really work.

Upvotes: 5

Views: 4744

Answers (1)

chepner
chepner

Reputation: 531205

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)

Upvotes: 5

Related Questions