Search code examples
pythonpython-3.xunit-testingpython-unittestpython-unittest.mock

Mocking class attribute that is a class imported from another module unittest python


I have following project structure:

├───pa
│   |───__init__.py
|   |─── a1.py
|   |─── test_a1.py
|
├───pb
│   |───__init__.py
|   |─── b1.py

With following code: b1.py:

class B1:

    def __init__(self):
        self.text = "Unmocked_B1"

    def get_text(self):
        return self.text

pb.__init__.py:

from .b1 import B1

a1.py:

from src.pb import B1


class A1:

    def __init__(self):
        self.b = B1()

    def get_text(self):
        return self.b.get_text()

pa.__init__.py:

from .a1 import A1

And I want to mock self.b in class A1 using unittest.mock.patch in such way that following code in test_a1.py will print "Mocked B1"

test_a1.py:

from unittest import TestCase
from unittest.mock import patch, PropertyMock
from . import A1


class TestB1(TestCase):

    @patch('Some args')
    def test_get_text(self, mocked_b1):
        # Some code
        
        a1 = A1()
        print(a1.get_text())  # Should print: Mocked B1

I tried both mocking B1 import with:

@patch('src.pb.b1.B1')
    def test_get_text(self, mocked_b1):
        mocked_b1.get_text.return_value = 'Mocked B1'

        a1 = A1()
        print(a1.get_text())  # Should print: Mocked B1

And mocking property of a1 with property mock:

@patch('pa.a1.A1.b', new_callable=PropertyMock)
    def test_get_text(self, mocked_b):
        mocked_b.get_text.return_value = 'Mocked B1'

        a1 = A1()
        print(a1.get_text())  # Should print: Mocked B1

Which does not seem to work even when I make attribute b inside A1 static instead of dynamic.

Is there a way to mock this attribute? It would be perfect if it would work on dynamic attribute as shown earlier.

I'm using python 3.10


Solution

  • Try the following syntax that uses patch.objectand context manager (instruction with):

    class TestB1(TestCase):
        def test_get_text(self):
            a1 = A1()
            with patch.object(a1, "b") as mocked_b1:
                mocked_b1.get_text.return_value = 'Mocked B1'
    
                print(a1.get_text())  # Prints: Mocked B1