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

Time:12-05

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

CodePudding user response:

Try the following sintax 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

CodePudding user response:

Thank you frankfalse for answer of course similar solution with patch decorator also works (I discovered it this morning):

@patch('src.pa.a1.B1')
def test_get_text(self, mocked_B1):
    instance = mocked_B1.return_value
    instance.get_text.return_value = 'Mocked B1'

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

Don't know if there is solution with property mock

  • Related