118

I'm having a fairly difficult time using mock in Python:

def method_under_test():
    r = requests.post("http://localhost/post")

    print r.ok # prints "<MagicMock name='post().ok' id='11111111'>"

    if r.ok:
       return StartResult()
    else:
       raise Exception()

class MethodUnderTestTest(TestCase):

    def test_method_under_test(self):
        with patch('requests.post') as patched_post:
            patched_post.return_value.ok = True

            result = method_under_test()

            self.assertEqual(type(result), StartResult,
                "Failed to return a StartResult.")

The test actually returns the right value, but r.ok is a Mock object, not True. How do you mock attributes in Python's mock library?

4 Answers 4

130

You need to use return_value and PropertyMock:

with patch('requests.post') as patched_post:
    type(patched_post.return_value).ok = PropertyMock(return_value=True)

This means: when calling requests.post, on the return value of that call, set a PropertyMock for the property ok to return the value True.

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

5 Comments

If I print the value of r.ok from in the method_under_test, I see <MagicMock name='post().ok' id='57360464'>, not True.
@TKKocheran: I have updated my answer. You also need to use a PropertyMock.
Why not simpy using patched_post.return_value = mock.Mock(ok=True)?
@lumbric Because you can use PropertyMock to assert that it was accessed like any other Mock object. By just assigning a value to the property you can't do that.
Just because Response.ok is a property doesn't mean the fake response also has to provide a property. The code under test one cares that r.ok evaluates to True or False; how you implement that in the mock shouldn't matter.
27

A compact and simple way to do it is to use new_callable patch's attribute to force patch to use PropertyMock instead of MagicMock to create the mock object. The other arguments passed to patch will be used to create PropertyMock object.

with patch('requests.post.ok', new_callable=PropertyMock, return_value=True) as mock_post:
    """Your test"""

Comments

22

With mock version '1.0.1' the simpler syntax mentioned in the question is supported and works as is!

Example code updated (py.test is used instead of unittest):

import mock
import requests


def method_under_test():
    r = requests.post("http://localhost/post")

    print r.ok

    if r.ok:
        return r.ok
    else:
        raise Exception()


def test_method_under_test():
    with mock.patch('requests.post') as patched_post:
        patched_post.return_value.ok = True

        result = method_under_test()
        assert result is True, "mock ok failed"

Run this code with: (make sure you install pytest)

$ py.test -s -v mock_attributes.py 
======= test session starts =======================
platform linux2 -- Python 2.7.10 -- py-1.4.30 -- pytest-2.7.2 -- /home/developer/miniconda/bin/python
rootdir: /home/developer/projects/learn/scripts/misc, inifile: 
plugins: httpbin, cov
collected 1 items 

mock_attributes.py::test_method_under_test True
PASSED

======= 1 passed in 0.03 seconds =================

2 Comments

FYI requests.post.ok is a property and not an attribute. If you try on simple object where ok is a simple attribute the syntax mentioned in the question works but for requests.post.ok object no: it will raise an AttributeError.
@Micheled'Amico thanks for your feedback, I tried please take a look ;-)
1

Another alternative using a decorator to mock the attribute

@mock.patch('path.to.your.Object')
def test_object(self, mock_object):
  mock_object.return_value.property_to_mock = 'your mock property value'

1 Comment

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.

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.