Mock attributes in Python mock?

100,177

Solution 1

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.

Solution 2

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"""

Solution 3

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 =================
Share:
100,177
Naftuli Kay
Author by

Naftuli Kay

Updated on December 12, 2020

Comments

  • Naftuli Kay
    Naftuli Kay over 3 years

    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?

  • Naftuli Kay
    Naftuli Kay almost 11 years
    If I print the value of r.ok from in the method_under_test, I see <MagicMock name='post().ok' id='57360464'>, not True.
  • Simeon Visser
    Simeon Visser almost 11 years
    @TKKocheran: I have updated my answer. You also need to use a PropertyMock.
  • philant
    philant about 9 years
    This does not provide an answer to the question. To critique or request clarification from an author, leave a comment below their post - you can always comment on your own posts, and once you have sufficient reputation you will be able to comment on any post.
  • Michele d'Amico
    Michele d'Amico about 9 years
    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.
  • howaryoo
    howaryoo over 8 years
    @philant thanks for your feedback, as proved by the example this is the up-to-date answer to the question and the syntax happens to be much simpler.
  • howaryoo
    howaryoo over 8 years
    @Micheled'Amico thanks for your feedback, I tried please take a look ;-)
  • lumbric
    lumbric over 7 years
    Why not simpy using patched_post.return_value = mock.Mock(ok=True)?
  • Bono
    Bono about 6 years
    @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.