Search code examples
pythonunit-testingmockingpython-unittestpython-unittest.mock

Python unittest returns MagicMock object instead of value


I have a program like this:

    class SomeClass:
        _attribute = None
    
        def __init__(self, attribute):
            self._attribute = attribute
    
        @property
        def attribute(self):
            print("abc")
            return self._attribute
    
    
    class SomeChildClass:
        context: SomeClass
    
        def __init__(self):
            pass
        
        def print_attribute(self):
            print(self.context.attribute)

I want to unittest the print_attribute method of SomeChildClass. Therefore I want to Mock both the SomeClass itself and the attribute property of this class.

When I use

    def test_print_attribute(self):
            with patch('app.some_class.SomeClass.attribute', new_callable=PropertyMock) as mock_attr:
                mock_attr.return_value = "TEST"
                some_class = SomeClass(attribute="abc")
                self.child_class = SomeChildClass()
                self.child_class.context = some_class
                self.child_class.print_attribute()

it is working and i get TEST as output. The problem with this solution is that a real instance of SomeClass needs to be created. My ultimate goal would be something like this:

    def test_print_attribute(self):
        with patch('app.some_class.SomeClass.attribute', new_callable=PropertyMock) as mock_attr:
            mock_attr.return_value = "TEST"
            some_class = MagicMock()
            self.child_class = SomeChildClass()
            self.child_class.context = some_class
            self.child_class.print_attribute()

Here I get only an object of MagicMock printed instead of TEST:

<MagicMock name='mock.attribute' id='2576201109456'>

I also tried this, which also returns a MagicMock object:

    @patch('app.some_class.SomeClass')
    def test_print_attribute(self, mock_some_class):
        child_class = SomeChildClass()
        child_class.context = mock_some_class
        mock_some_class.attribute.return_value = "TEST"
        child_class.print_attribute()
<MagicMock name='SomeClass.attribute' id='1701032644768'>

Solution

  • The solution was just to don't call the return_value method on the mock and access the attribute directly.

        @patch('app.some_class.SomeClass')
            def test_print_attribute(self, mock_class):
                child_class = SomeChildClass()
                child_class.context = mock_class
                mock_class.attribute = "TEST"
                child_class.print_attribute()
    

    If anyone could explain this behavior I will accept the answer :)