Python Test-mocking Notes

Russell Bateman
December 2014
last update:


Miscellaneous links

Function redirection for mocking

The replacement of os.remove() is for the current process and only for the current process. If you don’t restore the original function pointer when you are done, it's broken. Of course, it only matters if you are running a test suite and other tests would not expect the redirected function

foo.py:
'''
Demonstration of stubbing/mocking.
'''
import os

class Foo( object ):
  def removeMe( self, path ):
      os.remove( path )
fooMockTest.py:
'''
Demonstration, with foo.py, of how we can stub out os.remove().
'''
import unittest
import os
from foo import Foo

class FooTest( unittest.TestCase ):
  def setUp( self ):
      # stub out os.remove() here
      self.os_remove_ori = os.remove
      os.remove = my_remove

  def tearDown( self ):
      os.remove = self.os_remove_ori

  def testRemoveFoo( self ):
      foo = Foo()
      foo.removeMe( "/path/filename" )


def my_remove( filepath ):
  print "Didn't actually try to remove ", filepath

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

Mocking examples
~ $ python
Python 2.7.5 (default, Nov  3 2014, 14:26:24)
[GCC 4.8.3 20140911 (Red Hat 4.8.3-7)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> class Foo( object ):
...     def foo( self ):
...             print 'foo'
...
>>> from mock import patch
>>> with patch.object( Foo, 'foo', autospec=True ) as mock_foo:
...     mock_foo.return_value = 'bar'
...     foo = Foo()
...     foo.foo()
...
'bar'
>>> import mock
>>> class Production( object ):
...     def something( self ):
...             print 'something'
...
>>> real = Production()
>>> real.something = mock.MagicMock()
>>> real.something.return_value = 'changed'
>>> real.something()
'changed'

How to "unmock"...

In running a whole suite, the commented-out lines didn't work because they contaminated the definition of configuration.adjustConfigurationPath() for every test case after executing this one. Here's how to fix that problem. Note that

        configuration.adjustConfigurationPath = configuration.adjustConfigurationPath()

...is just bad science anyway.

    def testDATABASE_PATH_adjusted( self ):
        testUtilities.printTestCaseName( sys._getframe().f_code.co_name )
        mockObtainPracticeList()
        setUpDatabaseMocks()
        databasePath = globals.DATABASE_PATH

        # rig configuration to give us what we want and not find what we don't want...
        createAndFillDummyConfigurationFile( 'sqlite:///../funny-place/appliance.db' )
        # configuration.adjustConfigurationPath = Mock()
        # configuration.adjustConfigurationPath.return_value = CONFIGFILEPATH_FOR_TESTING
        p = patch( 'configuration.adjustConfigurationPath', new=MagicMock( return_value=CONFIGFILEPATH_FOR_TESTING ) )
        p.start()

        testUtilities.spinCommandLine( testFodder.SIMPLE_PUBLISH_DEF % testUtilities.getRandomId() )
        result = batch_runner.main( 'dummy' )

        p.stop()
        # (demonstrate that our mock has gone away...)
        # asdf = configuration.adjustConfigurationPath( 'asdf' )
        # configuration.adjustConfigurationPath = configuration.adjustConfigurationPath()

        assert globals.DATABASE_PATH != databasePath

Informed by: http://stackoverflow.com/questions/16310989/python-how-to-unmock-reset-mock-during-testing


Mocking methods...

Sort of a stupid example (since called with constants), but it illustrates the use of Mock.assert_called_with().

testInstall.py:
  .
  .
  .

  def test( self ):
    install.wget = Mock()
    result = install.main( dummy='crap' )
    install.wget.assert_called_with( url=install.ACTIVEMQ_TARBALL_URI, filename=install.TARBALL_NAME )
  .
  .
  .
install.py:
  .
  .
  .
  try:
    wget( url=ACTIVEMQ_TARBALL_URI, filename=TARBALL_NAME )
  .
  .
  .

Other Mock() methods include:

__init__( name, return_value, side_effect, spec )
reset_mock()

assert_called_with( *args, **argkw )
assert_called_once_with( *args, **argkw )
assert_has_calls( ... )
assert_any_calls( *args, **argkw )

called : Boolean
call_count : integer
call_args : tuple
call_args_list : list
method_calls : list
mock_calls : list