Patch a class – that is an attribute of an other class – to access its methods

Question:

I am trying to mock an attribute of a class Bar upon creation of its instance object test_obj. The attribute is an instance of another class called Foo.
Afterwards, I want to test a bar method from Bar class which is calling a search method of the Foo class.

I am having this error: AttributeError: 'str' object has no attribute 'search'. I understand it might be because I am mocking the foo attribute of Bar class with the str as MockedFoo.

My question is: how can I change my mocks so that I can access the methods of Foo class via the foo object of Bar class? The search method further must return a list (which I’m able to do) but I am unable to access this method first.

Here is my code:

import unittest
from mock import patch


class Foo(object):
    def __init__(self):
        self.foo = 'foo'
    
    def search(self):
        # do something

class Bar(object):
    def __init__(self):
        self.id = "123"
        self.name = "abc"
        self.foo = Foo()

    def bar(self):
        return self.foo.search()
    

def test_bar():
# initialization of Bar object
    with mock.patch('lib.lib_foo.Foo', return_value="MockedFoo")
        test_obj = Bar()
    test_obj.bar()

I have read a similar issue but that is for the access of the field. I am unable to adapt my code for accessing the methods.

Answers:

I have modified the instruction with mock.patch('lib.lib_foo.Foo', return_value="MockedFoo") in your test_bar() method, and I have set a list as return value for the method search of the mock object created.

Below there is my code:

import unittest
from unittest.mock import patch

class Foo(object):
    def __init__(self):
        self.foo = 'foo'

    def search(self):
        # do something
        pass

class Bar(object):
    def __init__(self):
        self.id = "123"
        self.name = "abc"
        self.foo = Foo()

    def bar(self):
        return self.foo.search()

class MyTestCase(unittest.TestCase):

    def test_bar(self):
        test_obj = Bar()
        with patch.object(test_obj, 'foo') as mock_foo:
            mock_foo.search.return_value = ['my', 'test', 'list']
            self.assertEqual(['my', 'test', 'list'], test_obj.bar())

if __name__ == '__main__':
    unittest.main()

Note that the test_obj object is created before the context manager with ....

Answered By: frankfalse
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.