Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why would mock be adding __nonzero__ method calls?

I've got a bit of code that makes a call to a series of methods on each item in a collection, and each method returns a boolean value indicating success = True/failure = False.

def monkey(some_collection, arg1, arg2):
    for item in some_collection:
        if not item.foo(arg1, arg2):
            continue
        if not item.bar(arg1, arg2):
            continue
        if not item.baz(arg1, arg2):
            continue

And, here's my unit test example:

import mock
def TestFoo(unittest.TestCase):
    def test_monkey(self):
        item = MagicMock()
        some_collection = [item]
        collection_calls = []
        foo_call = mock.call().foo(some_collection, 1, 2)
        bar_call = mock.call().bar(some_collection, 1, 2)
        baz_call = mock.call().baz(some_collection, 1, 2)
        collection_calls = [foo_call, bar_call, baz_call]                   
        my_module.monkey(some_collection, 1, 2)
        item.assert_has_calls(collection_calls) # requires the correct call order/sequence

Actual Calls

  1. call().foo(<MagicMock id='12345'>, 1, 2)
  2. call().foo.__nonzero__()

...

NOTE: This unit test fails because its seeing the __nonzero__() method calls.

Question

Why is it adding the nonzero method calls?

Clarification

I'm using mock, which is included in stdlib as of python 3.3.

like image 704
bitcycle Avatar asked Dec 26 '12 21:12

bitcycle


1 Answers

Short answer:

If your mocked function does not have an explicit return_value or side_effect that returns, you will see this as Python attempts to evaluate the truthiness of your mock. Ensure your mock has one.

Either initialise it with one:

item = MagicMock(return_value=True)

or add one in:

item.return_value = True

Explanation:

When you do if not x:, you are probably thinking if x is False.

Python doesn't actually do this - it does if bool(x) is False (this is Python's truthiness concept - that values evaluate to True or False), and bool(x) is actually a call to x.__nonzero__() (or x.__bool__() in 3.x).

This is to provide Python's nice if behaviour - when you do if []:, a lot of languages would treat any object as True, but Python is designed to make code readable, so it delegates and a list's __nonzero__() method will return False if it is empty. This allows for more code that reads more naturally, and explains why you will see these calls.

like image 180
Gareth Latty Avatar answered Oct 08 '22 10:10

Gareth Latty