'How to mock method of a mocked class

Changed the titel to a more common one. I guess the problem is not that class specific.

I want to mock google.cloud.pubsub_v1.SubscriberClient I want to set a fake return value when calling the pull function of the client.

prod code: from google.cloud import pubsub_v1

def open_subscription() -> None:
    with pubsub_v1.SubscriberClient() as subscriber:
        logger.info(f'Listening for messages on {config.SUBSCRIPTION_NAME}', {'operation': {'first': True}})

        while True:
            # get messages
            response = subscriber.pull(
                request = {
                    'subscription': config.SUBSCRIPTION_NAME,
                    'max_messages': config.MAX_MESSAGES
                }
            )

from the prod code above I want to set the return value for calling the pull method. I am creating a pull-response object in the test code.

test code:

import unittest
from unittest.mock import MagicMock, patch
from app.pubsub import pubsub_service
from google import pubsub_v1
import json
        
class TestPubSubService(unittest.TestCase):
    
    def create_test_message(self):
        message_string = '{"testKey": "testValue"}'
        message_json = json.dumps(message_string, indent=2)
        message_data = message_json.encode('utf-8')
        
        pubsub_message = pubsub_v1.PubsubMessage()
        pubsub_message.data = message_data

        received_message = pubsub_v1.ReceivedMessage()
        received_message.ack_id = "testId"
        received_message.message = pubsub_message

        return received_message
    
    def create_test_pull_response(self, received_message):
        pull_response = pubsub_v1.PullResponse()
        pull_response.received_messages = [received_message]
        return pull_response
    
    @patch('app.pubsub.pubsub_service.pubsub_v1.SubscriberClient')
    def test_open_subscription(self, mock_subscriber):

        test_message = self.create_test_message()
        pull_response = self.create_test_pull_response(test_message)
        mock_subscriber.return_value.pull.return_value = MagicMock(return_value = pull_response)

        pubsub_service.open_subscription()

At least the MagicMock is in place (without using the patch the real subscriber is in place). So basically I would say I mocked the subscriberClient. But I cannot set the return_value for calls to the pull method. But there wont be a pull retur value. All I get is another magicMock created.

I do not get it why it is not working. As most stuff I read we usually have to call 'return_value' on the mock, append the name of either the field or function to be set, append that ones 'return_value' and set a value viea MagicMock. The format should be: mockFirst.return_value.second.return_value.third.return_value = Mock(return_value = the_value_to_return)

Hopefully you can explain me what I am doing wrong. Thanks.

edit: tried also the following ones which where the answers in other posts: Mocking Method Calls In Python Mock a method of a mocked object in Python?

mock_subscriber.pull.return_value = pull_response
mock_subscriber.return_value.pull.return_value = pull_response

none seems to work. the return value of the pull method stays to be a magicMock.

And this is how it looks like in debugging (hovering over response): enter image description here



Solution 1:[1]

I faced the same issue. But can get idea from the details inside MagicMock.

Try to set return value (based on your screenshot)

mock_subscriber.__enter__().pull.return_value = pull_response

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Brij