1

I have a parent object, which receives a connection object.

I run a method on this connection object to generate a config object. When I mock this, instead of Foo I get:

<Mock name='mock().get_properties().property_c' id='1910891784064'>

Code:

# Real

class ClassC:
    property_c = "Foo"

class ClassB:
    def __init__(self):
        pass

    def login(self):
        print("Logged in...")

    def get_properties(self):
        return ClassC()

class ClassA:
    def __init__(self, conn):
        self.conn = conn
        self.conn.login()

a = ClassA(conn=ClassB()) >>> Logged in...
result = a.conn.get_properties()
print(result.property_c) >>> Foo


# Mocked

from unittest.mock import Mock
mock_b = Mock()
mock_c = Mock()
mock_c.property_c.return_value = "Foo_Mock"
mock_b.get_properties.return_value = mock_c

a = ClassA(conn=mock_b())
result = a.conn.get_properties()
print(result.property_c) >>> Output shown above

How do I mock this properly?

Edit1 - The suggested duplicate S.O answer only partially answers the question.

Edit2 - I forgot to include the mock login behaviour

mock_b.login.return_value = print("Logged in...") awesoon's answer still works with this modification:

    mock_b = Mock()
    mock_c = Mock()
    type(mock_c).property_c = PropertyMock(return_value="Foo_Mock")
    mock_b.get_properties.return_value = mock_c
    mock_b.login.return_value = print("Logged in...")
8
  • Possible duplicate of Mock attributes in Python mock? Commented May 6, 2019 at 10:31
  • I see how PropertyMock can be used for ClassC, but how do I inject it into ClassB? Commented May 6, 2019 at 10:53
  • type(mock_c).property_c = PropertyMock(return_value="Foo_Mock") and mock_b.get_properties().property_c shall do the trick. Note I did not call mock_b() Commented May 6, 2019 at 10:59
  • I still get <Mock name='mock().get_properties().property_c' id='1910891784064'> Commented May 6, 2019 at 11:09
  • 1
    Check this one: ideone.com/aCy9Ye Commented May 6, 2019 at 11:13

2 Answers 2

2

There are two issues in your code:

  1. Do not call mock_b when passing to ClassA (a = ClassA(conn=mock_b()). Calling a mock will create another mock and it will not contain changes made to mock_b.

  2. Use PropertyMock to mimic attribute:

    type(mock_c).property_c = PropertyMock(return_value="Foo_Mock")
    

    You can also set attribute explicitly, but PropertyMock is more flexible.

The final code is:

from unittest.mock import Mock, PropertyMock
mock_b = Mock()
mock_c = Mock()
type(mock_c).property_c = PropertyMock(return_value="Foo_Mock")
mock_b.get_properties.return_value = mock_c

print(mock_b.get_properties().property_c)

Outputs:

Foo_Mock
Sign up to request clarification or add additional context in comments.

Comments

0

I think problem is with this line:

a = ClassA(conn=mock_b())

You are creating here new mock. If you try below code:

mock_b = Mock(name='mock_b')
a = ClassA(conn=mock_b())
print(mock_b)
print(a.conn)

You will see that those, are two different mocks, with different IDs (for example):

<Mock name='mock_b' id='48709360'>
<Mock name='mock_b()' id='48709528'>

So frits of all, you need to replace conn=mock_b() by conn=mock_b.

If you fix that then you could call:

print(result.property_c())  # notice extra parathesis.

It would look better if you add get_property method to ClassC, and then mock method get_property.

2 Comments

I cannot structure it your way. I'm bound by the api
Ok, so you can simply change your 2 lines in your test: mock_c.property_c.return_value = "Foo_Mock" >> mock_c.property_c = "Foo_Mock" and: a = ClassA(conn=mock_b()) >> a = ClassA(conn=mock_b)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.