PfunnyGuy
PfunnyGuy

Reputation: 948

Python patch mock appears to be called, but assert fails

I'm using Python 2.6.6

What I'm trying to do is replace the creation of an object with a Mock, to make sure that the correct calls are made. Should be straightforward.

My Module:

import dir.SubModule

class Cls( object ):
    def Start( self ):
        self.__obj = dir.SubModule.SubCls()
        self.__obj.foo()

My Test:

import MyModule
import unittest
from mock import Mock, MagicMock, patch

class MyTest( unittest.TestCase ):
    def setUp( self ):
        self.uut = MyModule.Cls()

    def test_one( self ):
        with patch( 'dir.SubModule.SubCls', spec=True ) as mockObj:
            print "mock calls before"
            print mockObj.mock_calls
            self.uut.Start()

            print "called: "      + str( mockObj.called )
            print "foo called: " + str( mockObj.foo.called )
            print "call_count: "  + str( mockObj.call_count )
            print "call_args: "   + str( mockObj.call_args )
            print "args_list: "   + str( mockObj.call_args_list )
            print "mock calls:\n" + str( mockObj.mock_calls )
            print "method calls:\n " + str( mockObj.method_calls )

The output is:

mock calls before:
[]
called: True
foo called: False
call_count: 1
call_args: call()
args_list: [call()]
mock calls:
[call(), call().foo()]
method calls:
[]

Yet the test fails:

AssertionError: Expected call: foo()
Not called

I don't get how the mock can report that the calls were made, but I can't assert that they were called. What am I missing?

EDIT: After adding in reporting of all the metrics, it seems there is something fundamental that I'm misunderstanding about python mocks. If foo() is in the calls list, then why is the call count only 1, and why does foo.called report 'False'?

Upvotes: 1

Views: 9838

Answers (3)

Peter K
Peter K

Reputation: 2484

Mock assert syntax has many quirks. To handle it I wrote a helper library to generate the asserts for me.

Here is how you would use it for your test method:

import MyModule
import unittest
from mock import patch

class MyTest( unittest.TestCase ):
    def setUp( self ):
        self.uut = MyModule.Cls()

    def test_one( self ):
        with patch( 'dir.SubModule.SubCls', spec=True ) as mockObj:
            self.uut.Start()

            # calls to generate_asserts, put this after the 'act'
            import mock_autogen.generator
            print(mock_autogen.generator.generate_asserts(mockObj))

The output you get is:

assert 1 == mockObj.call_count
mockObj.assert_called_once_with()
mockObj.return_value.foo.assert_called_once_with()

Which correlates to the __init__ call, followed by the call to foo of the newly created object.

So no more need to figure out the exact syntax by yourself, just apply the tool :)

Upvotes: 0

PfunnyGuy
PfunnyGuy

Reputation: 948

Here is the link: link

My simple fix:

with patch( 'class' ) as mockCreator:
    mockObj = mockCreator.return_value

Then I can use 'mockObj' willy-nilly.

I ran across the linked solution last week when I was first learning about mocks - a bit too soon for the information to quite sink in.

Upvotes: 0

user2357112
user2357112

Reputation: 282026

mockObj.foo is never called in this test. self.uut.Start() calls mockObj, creating a new mock, and then calls that mock's foo method. If you want to assert that this call happened, you will need to access the correct object:

mockObj.return_value.foo.assert_called_with()

Upvotes: 9

Related Questions