Python Testing Notes

Russell Bateman
December 2014
last update:


Miscellaneous links

Running a unit test from the command line
if __name__ == '__main__':
    unittest.main()

This allows us to run all of the test code just by running the file. Running it with no options is the most terse, and running with -v is more verbose, showing which tests ran.


Getting a unit test to work

This did not go swimmingly. In fact, on my Fedora 20 host at my place of employment, it still does not work.

See my stackoverflow post.

I installed a fresh Linux Mint 17, fresh Luna Eclipse IDE for Java EE Developers with PyDev, and it worked fine, first time.

from unittest import TestCase, main as demo_main

def IsOdd( n ):
  return n % 2 == 1

class IsOddTests( TestCase ):
  def testOne( self ):
      self.failUnless( IsOdd( 1 ) )
  def testTwo( self ):
      self.failIf( IsOdd( 2 ) )

def main():
  demo_main()

if __name__ == '__main__':
  main()

Unit testing
test/utilsTest.py:
import os
import unittest
from artman import artman
import artman.utils as utils

class UtilTest( unittest.TestCase ):
  def testLookForArtmanRoot_findNone( self ):
      where = utils.lookForArtmanRoot()
      assert where == None

  def testLookForArtmanRoot_findOne( self ):
      artmanTestDirectory = os.path.join( "..", artman.ARTMAN_DIRECTORY );

      if not os.path.exists( artmanTestDirectory ):
        os.makedirs( artmanTestDirectory )

      where = utils.lookForArtmanRoot()
      assert where != None

      os.rmdir( artmanTestDirectory )

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

Unit testing, skip test case
    @unittest.skip( 'This is broken for now.' )
    def testGetWorld1( self ):
        pass

TDD

Python TDD example


Unit-test aids for printing (and not printing)
import unittest
PRINT = True  # makes print statements come out for inspection; set False when done debugging

class SomeTest( unittest.TestCase ):

  def testOkay( self ):
     printTestCaseName( sys._getframe().f_code.co_name )

def printTestCaseName( functionName ):
  if PRINT:
    print 'Running test case %s...' % functionName

Unit testing by asserting an exception raised
    def testArgumentsNoStop( self ):
        setUpPageXmlResultsMock( 'This is a test' )
        pager  = PagingResultsController()
        actual = pager.viewResult( 'fodder', '.xml', start=1 )
        tearDownPageXmlResultsMock()

    def testArgumentsBadStop( self ):
        setUpPageXmlResultsMock( 'This is a test' )
        pager = PagingResultsController()
        self.assertRaises( Exception, pager.viewResult, 'fodder', '.xml', start=1, stop='stop here' )
        tearDownPageXmlResultsMock()

unittest @classmethod example

import unittest
import os
import imp
import sys
import tempfile

FODDER = '''

   This, that, and the other thing 
   This, that, and the other thing 
   This, that, and the other thing 
   This, that, and the other thing 
   This, that, and the other thing 
   This, that, and the other thing 
   This, that, and the other thing 
   This, that, and the other thing 
   This, that, and the other thing 
   This, that, and the other thing 

'''

class TestPager( unittest.TestCase ):
    @classmethod
    def setUpClass( TestPager ):
        pythonpaths = os.environ[ 'PYTHONPATH' ].split( os.pathsep )
        projectpath = pythonpaths[ 0 ]
        pageresultspath = projectpath + os.sep + 'code.webapp.searchui.controllers.pageresults'.replace( '.', os.sep ) + '.py'
        TestPager.pageresults = loadPageResultsModule( pageresultspath )
        ( TestPager.fd, TestPager.path ) = tempfile.mkstemp()
        os.write( TestPager.fd, FODDER )
        os.close( TestPager.fd )

    @classmethod
    def tearDownClass( TestPager ):
        os.remove( TestPager.path )

    def test( self ):
        pageXmlResults = self.pageresults.pageXmlResults
        actual = pageXmlResults( self.path, start=2, limit=4 )
        print actual
        assert '02' in actual
        assert '03' in actual
        assert '04' in actual
        assert '05' in actual
        assert '01' not in actual
        assert '06' not in actual

def loadPageResultsModule( path ):
    module, extension = os.path.splitext( os.path.split( path ) [ -1 ] )
    pageresults = imp.load_source( module, path )
    return pageresults

Python unit-testing utilities

These are my own derivations.

testUtilities.py:
import sys

CONSOLE_WIDTH = 80

# Change this to False to make testing output less verbose. Always
# use printOrNot() to print so it won't come out when less verbose.
PRINT = True

def printOrNot( thing='' ):
  if not PRINT:
    return
  print thing

def spinCommandLine( commandLine ):
  ''' Sets up system sys.argv for use in a main called later during test. '''
  printOrNot( 'Command line: %s' % commandLine )
  del sys.argv[ 1: ]
  sys.argv[ 0 ] = 'main.py'
  sys.argv.extend( commandLine.split( ' ' ) )

def printTestCaseName( functionName ):
  '''
  Call thus:
  testUtilities.printTestCaseName( sys._getframe().f_code.co_name )
  --helps you sort through unit test output by creating a banner before each test case.
  '''
  if not PRINT:
    return
  banner = '\nRunning test case %s ' % functionName
  length = len( banner )
  sys.stdout.write( banner )
  for col in range( length, CONSOLE_WIDTH ):
    sys.stdout.write( '-' )
  sys.stdout.write( '\n' )
  sys.stdout.flush()

# vim: set tabstop=2 shiftwidth=2 expandtab:
unitTest.py:

Change the name, UnitTest, to something real, of course.

import unittest

import main

class UnitTest( unittest.TestCase ):

  @classmethod
  def setUpClass( UnitTest ):
    # once before running anything set-up code here...
    pass

  def setUp( self ):
    # before each test case set-up code here...
    pass

  def tearDown( self ):
    # after each test case tear-down code here...
    pass

  ''' Test cases ------------------------------------------------------------ '''
  def test( self ):
    testUtilities.spinCommandLine( "Some command line here" )
    main.main( 'dummy' )

  @unittest.skip( "how to skip this test..." )
  def testSkip( self ):
    pass

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

# vim: set tabstop=2 shiftwidth=2 expandtab:
main.py:

Subject under test; change this to something real.

def main( dummy ):
  ''' sys.argv[] is available here... '''
  pass

if __name__ == "__main__":
  if len( sys.argv ) <= 1:
    sys.exit( main( '--help' ) )
  elif len( sys.argv ) >= 1:
    sys.exit( main( sys.argv ) )

# vim: set tabstop=2 shiftwidth=2 expandtab: