Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python mock class with function and property

I am having some trouble patching a class with a function and a property.

The project structure I am working with is as follows:

project
|- src
|  |- logic
|  |  |- sub_logic
|  |  |  | __init__.py
|  |  |  | cache.py
|  |  |  | manager.py
|  |  | __init__.py  
|- test
|  | test.py  

My cache file looks like this

class Cache(object):
    def __init__(self, val):
        self._val = val

    @property
    def Val(self):
        return self._val

    def other_function(self):
        return False

The manager file looks like this

from cache import Cache


class Manager(object):
    def __init__(self):
        self._cache = Cache(20)

    def do_something(self):
        if self._cache.Val != 20:
            raise ValueError(u"Val is not 20")

        return True

    def do_something_else(self):
        if self._cache.other_function():
            raise ValueError(u"Something is True")

The tests I tried to make are the following:

from unittest import TestCase
from mock import PropertyMock, patch

from logic.sub_logic.manager import Manager
from logic.sub_logic.cache import Cache


class ManagerTestCase(TestCase):

    def test_01_cache(self):
        manager = Manager()
        self.assertEqual(manager.do_something(), True)

    @patch('logic.sub_logic.manager.Cache.Val', new_callable=PropertyMock)
    def test_02_cache(self, property_mock):
        property_mock.return_value = 20
        manager = Manager()
        self.assertEqual(manager.do_something(), True)

    @patch('logic.sub_logic.manager.Cache', spec=Cache)
    def test_03_cache(self, cache_mock):
        cache_mock.other_function.return_value = True
        manager = Manager()
        with self.assertRaises(ValueError):
            manager.do_something_else()

    @patch('logic.sub_logic.manager.Cache', spec=Cache)
    def test_04_cache(self, cache_mock):
        cache_mock.other_function.return_value = True
        cache_mock.Val = PropertyMock()
        cache_mock.Val.return_value = 20

        manager = Manager()
        with self.assertRaises(ValueError):
            manager.do_something_else()
        self.assertEqual(manager.do_something(), True)

    @patch('logic.sub_logic.manager.Cache.Val', new_callable=PropertyMock)
    @patch('logic.sub_logic.manager.Cache', spec=Cache)
    def test_05_cache(self, cache_mock, property_mock):
        cache_mock.other_function.return_value = True
        property_mock.return_value = 20
        manager = Manager()
        with self.assertRaises(ValueError):
            manager.do_something_else()
        self.assertEqual(manager.do_something(), True)

    @patch('logic.sub_logic.manager.Cache', spec=Cache)
    def test_06_cache(self, cache_mock):
        cache_mock.other_function.return_value = True
        cache_mock.Val = 20

        manager = Manager()
        with self.assertRaises(ValueError):
            manager.do_something_else()
        self.assertEqual(manager.do_something(), True)

The problem is that test_04_cache and test_05_cache are not working. When debugging the the tests, the mock parameter provided is as I expected it to be. But the Manager creates a MagicMock where the property Val is not a PropertyMock but also a MagicMock.

I inspected test_06_cache in PyCharm Debugger which reports the following:

  • cache_mock.Val = {int}20
  • manager._cache.Val = {MagicMock}<MagicMock name='Cache().Val' id='61044848'>

Am I missing something? Or is it not possible?

like image 582
KlemensE Avatar asked Oct 25 '25 05:10

KlemensE


1 Answers

When you use

@patch('logic.sub_logic.manager.Cache', spec=Cache)

the resulting mock is for the class. Your Manager then creates an instance by calling that class, in __init__. You should therefore be setting attributes and return values on mock_cache() (note parentheses), which is the "instance" that will be assigned to manager._cache, rather than on the "class" mock_cache.

Note that, as the manager doesn't know that the cache is using a @property, you can just set:

mock_cache().Val = 20
like image 113
jonrsharpe Avatar answered Oct 26 '25 20:10

jonrsharpe



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!