1

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

piotre10
  • 43
  • 4

2 Answers2

1

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
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
frankfalse
  • 1,553
  • 1
  • 4
  • 17
0

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

piotre10
  • 43
  • 4
  • 1
    Your solution mocks the class `B1` **inside** the module `a1` and after that you get an instance of `B1`. It is an other example of Mocking in Python (a bit strange in my opinion). My solution (always in my opinion - but I'm not a guru!) mocks the attribute `b` of the instance a1 of class A1 and this seams what you ask in your question title. – frankfalse Dec 05 '22 at 10:31