-1

I would like to know the difference between pg.connect and pg.connect() statements.

pg is a psycopg2 mock object and when I display the dir, it shows connect as its method.

class TestMock(unittest.TestCase):
    def setUp(self):
        pass

    def test_mock_calls(self):
        import pprint
        pg = mock.MagicMock(psycopg2)
        print 'pg() object ', pg()
        print 'dir of pg = ', pprint.pprint(dir(pg))

        print 'dir of pg = ', pg.connect
        print 'dir of pg.connect = ', pprint.pprint(dir(pg.connect))

        print 'dir of pg = ', pg.connect()
        print 'dir of pg.connect() = ', 
        pprint.pprint(dir(pg.connect()))

Updates:- One(pg.connect) is accessing the attribute and other one(pg.connect()) calling the function.

Now, how can I set the return_value of the execute method?

For example:- I need to mock the response of my execute method.

so, it should be pg.connect().cursor().execute.side_effect = someexception()

user1050619
  • 19,822
  • 85
  • 237
  • 413
  • What's the difference between accessing an attribute and calling it? – jonrsharpe Jun 05 '18 at 16:05
  • yes, thats correct. – user1050619 Jun 05 '18 at 16:05
  • So how can that be answered without repeating the question? One is just *accessing* the attribute... the other is *calling* it, and giving you the return value. – jonrsharpe Jun 05 '18 at 16:06
  • thanks. I just updated my question to understand how to set the return value of my execute method. – user1050619 Jun 05 '18 at 16:11
  • And how does that relate to the rest of what you've written? What has led you to ask this in the first place; what did you try, and what happened? Where is the code under test, how does that mock get in there at all? – jonrsharpe Jun 05 '18 at 16:16
  • @jonsharpe Here is more detailed explanation on my issue...https://stackoverflow.com/questions/50705038/python-mock-postgres-connection .. I was planning to go step by step but I think its better to give a bigger context of my issue. – user1050619 Jun 05 '18 at 16:39

1 Answers1

0

Python is pretty literal minded. The expression pg.connect() translates to:

  1. Look up the name pg
  2. Look up the attribute connect on that.
  3. Call it with empty arguments.

So an expression pg.connect will obtain the method, but not call it. And, in fact, you can use that method as a regular value and pass it around, so my_connect = pg.connect is entirely valid, and you can later call my_connect().

We can see what's going on with the dis module:

import dis
dis.dis(lambda: pg.connect())

 1           0 LOAD_GLOBAL              0 (pg)
             2 LOAD_ATTR                1 (connect)
             4 CALL_FUNCTION            0
             6 RETURN_VALUE

Since Mock wants to pretend to be different objects, it is overriding the standard __getattribute__ and __call__ and __eq__ dunder methods. While objects have a normal way of providing attributes, __getattribute__ lets a class do whatever it wants if an attribute is requested.

When you call it as Mock(spec=psycopg2), it's going to do two main things differently than Mock():

  1. It adjusts its __class__ field to claim to be psycopg2.
  2. Check psycopg2 for valid properties and raise AttributeError if you try to get a field that isn't present.

But beyond that, it still behaves as a regular mock. If you call yourMock.foo, it doesn't know if it's a property or a method. It just returns a new Mock instance, and because that instance is callable, you can call it like a function.

Update: If you set spec on Mock, it's restricting the available attributes. To add an attribute not provided by spec, you just need to set it at init time:

cursor_mock = Mock(side_effect=psycopg2.DatabaseError)
Mock(spec=psycopg2.connect, cursor=cursor_mock)

You also wanted this behavior: pg.connect().cursor().execute.side_effect = someexception(). Generally, you'd alter the return_value:

cursor_mock = Mock()
cursor_mock.return_value.execute.side_effect = SomeException
# Same as:
cursor_mock = Mock(return_value=Mock(execute=Mock(side_effect=SomeException)))

You can also do cursor_mock().execute.side_effect = SomeException, but that pollutes cursor_mock.mock_calls.

Small point: I use Mock for brevity, and MagicMock is the same, simply adding some dunder methods.

Ben
  • 836
  • 8
  • 18