2

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.

Jerry
  • 563
  • 1
  • 6
  • 22
  • 1
    `decorator` has already been called by the time your test runs; it runs immediately after `get_value` is defined. It's too late to mock it. – chepner Oct 01 '20 at 12:14

1 Answers1

4

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)
chepner
  • 497,756
  • 71
  • 530
  • 681
  • 1
    Great answer. In addition I would like to point out that unit testing should only be used for public interfaces and stable concepts. Testing implementation details - for example testing a wrapped function - can limit the changeability of said code, as these test cases are easy to break. – Attila Viniczai Oct 01 '20 at 12:38
  • Thanks for the answer! However the test case I provided might be a bit oversimplified. In my use case the decorated function has more complex logic that needs to run. I suppose I need to rethink my approach here – Jerry Oct 01 '20 at 12:41