6

I'm having some trouble understanding why the following code does not pass:

test.py

import mock
import unittest

from foo import Foo


class TestFoo(unittest.TestCase):
    @mock.patch('foo.Bar')
    def test_foo_add(self, Bar):
        foo = Foo()
        foo.add(2, 2)
        Bar.add.assert_called_with(2, 2)


if __name__ == '__main__':
    unittest.main()

foo.py

from bar import Bar

class Foo(object):
    def add(self, x, y):
        bar = Bar()
        return bar.add(x, y)

bar.py

class Bar(object):
    def add(self, x, y):
        print('b.Bar --> Adding {} + {}'.format(x, y))
        return x + y

In the code, Foo.add creates an instance of Bar and returns the result of Bar.add when invoked. Why does testing assert_called_with for Bar.add fail? I believe I am mocking the Bar in the correct place (I am mocking foo.Bar because that is the namespace which it is being looked up, rather than bar.Bar).

Traceback (most recent call last): File "/Users/iain/PycharmProjects/testing/venv/lib/python2.7/site-packages/mock.py", line 1201, in patched return func(*args, **keywargs) File "test.py", line 12, in test_a_b fake_Bar.add.assert_called_with(2, 2) File "/Users/iain/PycharmProjects/testing/venv/lib/python2.7/site-packages/mock.py", line 831, in assert_called_with raise AssertionError('Expected call: %s\nNot called' % (expected,)) AssertionError: Expected call: add(2, 2) Not called

2 Answers 2

13

You are mocking the method call in the right place. However, since you are calling the method from an instance, it is a bound method, and thus receives the instance as the first argument (the self parameter), in addition to all the other arguments.

Edit: Since Bar is replaced with a Mock instance, Bar().add does not know it's a method (and hence is not bound to anything). In other words, Bar is a Mock, Bar() is a Mock, and Bar().add is also a Mock. bar.add is thus a newly created mock, called with arguments (2, 2). One way of asserting this call would be:

@mock.patch('foo.Bar')
def test_foo_add(self, Bar):
    foo = Foo()
    foo.add(2, 2)
    Bar.return_value.add.assert_called_with(2, 2)

Depending on how your actual code looks, you may want to instead mock the method rather than the class:

@mock.patch('foo.Bar.add')
def test_foo_add(self, bar_add):
    foo = Foo()
    foo.add(2, 2)
    bar_add.assert_called_with(2, 2)
Sign up to request clarification or add additional context in comments.

4 Comments

I've modified the test so that foo is passed as the first arg to Bar.add.assert_called_with() but still the test fails? The output reads the same (AssertionError: Expected call: add(<foo.Foo object at 0x102108a50>, 2, 2) Not called)
Whoops, my sample code was totally wrong. Will update in a few with the proper answer. Sorry!
Thanks! I think I fell made the mistake of thinking that if I mocked foo.Bar then all of its attributes and methods remained intact and unchanged unless I explicitly changed their behaviour . Would this be a case where one might use the mock.patch.object method instead?
patch.object and patch have identical functionality; they simply use a different way of referring to the object to patch. To patch Bar in the foo module, you would use mock.patch(foo, 'Bar'), and the results would be the same. I don't know of a pre-built way of getting the behavior you want, unfortunately.
-2

I believe your problem would be solved like this:

from bar import Bar

class Foo(object):
    def add(self, x, y):
        self.bar = Bar()
        return self.bar.add(x, y)

Comments

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.