pythonunit-testingmockingattributespytest

How to Mock Attributes & Properties of Return Values with patch()


test_client/wclient.py

import json
import requests
client = requests.session()

def setup():
    response = REST_CLIENT.post(
        "https://placeholder.com",
        auth=(placeholder, placeholder),
        data={"grant_type": "client_credentials"},
    )

    status_code = response.status_code
    if status_code in OK_STATUS:
        payload = json.loads(response.content, object_pairs_hook=OrderedDict)
    else:
        payload = response.text
        msg = (
            "Status Code %s" % status_code
        )
        logger.error(msg)
        raise ValueError(msg)

    return payload["access_token"]

Test File: test_client/test_client.py

import mock
import wclient
@mock.patch("test_client.wclient")
def test_taxes_pitney_bowes_setup_success(resp):
    resp.return_value.post.return_value.status_code = "200"
    wclient.pitney_bowes_setup()

Status Code <MagicMock name='REST_CLIENT.post().status_code' id='4868492200'>

How can I mock the module's methods and attributes with mock.patch()? I've read through pages of stack overflow posts but I'm getting confused with all the different ways to enforce a magic mock.

I've tried mocking:

resp.return_value.post.return_value.status_code
resp.return_value.post.return_value.status_code.return_value
resp.post.return_value.status_code
resp.post.return_value.status_code.return_value
resp.post.status_code
resp.post.status_code.return_value

Solution

  • I think there are numerous ways of actually doing the mock (see the many methods in Mocking Method Calls In Python). The way I like to do it and find easy for simple mocks is:

    For functions: @patch('module.print', lambda x: None)

    For attributes: @patch('module.cwd', os.path.join(os.getcwd(), "folder"))

    This blog post might be helpful for you: https://medium.com/uckey/how-mock-patch-decorator-works-in-python-37acd8b78ae.

    Let me know if you have more questions.

    Edit: To add multiple mocks just add another attribute:

    import wclient
    @mock.patch("test_client.wclient")
    @mock.patch("another_attribute", "value")
    @mock.patch("another_function", lambda x, y: x + y)
    def test_taxes_pitney_bowes_setup_success(resp):
        resp.return_value.post.return_value.status_code = "200"
        wclient.pitney_bowes_setup()