Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Patching class instance attribute which is a property of another class

#atomlib/atomtools.py

class AtomTools:
    def __init__(self):
        self.atomcachehelper = None

    @property
    def cache(self):
        if not self.atomcachehelper:
             from atomlib.AtomCacheHelper import AtomCacheHelper
             self.atomcachehelper = AtomCacheHelper()
        return self.atomcachehelper


# wdm_oms_failures_detector.py 

import pandas as pd
from atomlib.atomtools import atomtools as atom

class WdmNetworkFailureDetector(object):
    def __init__(self,):
        self._cache_helper = atom.cache

    def update_wdm_cache_data(self) -> pd.DataFrame:
        df_oms = self._cache_helper.query_to_df()
        return df_oms

# conftest.py

import pytest

@pytest.fixture(scope='module')
def df_oms():
    path = 'some_path.csv'
    df_oms = pd.read_csv(path)
    return df_oms


# test_wdm_oms_failures_detector.py

from unittest.mock import patch, PropertyMock, call
from wdm_oms_failures_detector WdmNetworkFailureDetector

wnfd = WdmNetworkFailureDetector()

class TestWdmNetworkFailureDetector(object):

    def test_get_mol_objects(self):
        pass

    @patch('wdm_oms_failures_detector.atom.cache')
    def test_update_wdm_cache_data(self, mocked_cache, df_oms):

        # Setup
        mocked_cache.return_value.query_to_df.return_value = df_oms
        # Exercise
        wnfd.update_wdm_cache_data()
        # Verify
        mocked_cache.return_value.query_to_df.assert_called_once()

I am trying to patch WdmNetworkFailureDetector class' instance attribute atom.cache, which is a property of atomtools which returns and AtomCacheHelper object. Then I'd like to mock AtomCacheHelper's query_to_df method and return the fixture instead. I am not sure how to do it properly, I tried several ways, most of them resulted in the same AttributeErrors:

....
     elif kwargs:
            # can't set keyword args when we aren't creating the mock
            # XXXX If new is a Mock we could call new.configure_mock(**kwargs)
            raise TypeError("Can't pass kwargs to a mock we aren't creating")

        new_attr = new

        self.temp_original = original
        self.is_local = local
>       setattr(self.target, self.attribute, new_attr)
E       AttributeError: can't set attribute

....
self = <unittest.mock._patch object at 0x000001E9B1618940>, exc_info = (<class 'AttributeError'>, AttributeError("can't set attribute",), <traceback object at 0x000001E9B17EEF48>)

    def __exit__(self, *exc_info):
        """Undo the patch."""
        if not _is_started(self):
            raise RuntimeError('stop called on unstarted patcher')

        if self.is_local and self.temp_original is not DEFAULT:
            setattr(self.target, self.attribute, self.temp_original)
        else:
>           delattr(self.target, self.attribute)
E           AttributeError: can't delete attribute
like image 992
hk_03 Avatar asked Oct 29 '25 14:10

hk_03


1 Answers

The problem is that you instantiate your tested class outside of the patching, so the cache helper is initialized with the real object, and patching it later does not change that, as it is already assigned to the variable. You have to do the instantiation during patching:

test_wdm_oms_failures_detector.py

from unittest.mock import patch

from wdm_oms_failures_detector import WdmNetworkFailureDetector


class TestWdmNetworkFailureDetector(object):

    @patch('wdm_oms_failures_detector.atom.cache')
    def test_update_wdm_cache_data(self, mocked_cache, df_oms):
        mocked_cache.return_value.query_to_df.return_value = df_oms
        wnfd = WdmNetworkFailureDetector()  # now the cache helper is set to the mock 
        wnfd.update_wdm_cache_data()
        mocked_cache.query_to_df.assert_called_once()
like image 88
MrBean Bremen Avatar answered Nov 01 '25 05:11

MrBean Bremen



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!