Awesome Python Testing


Workflow Evolution

1. edit, edit, commit
2. edit, commit
3. todo, edit, commit
4. todo, edit, test, commit
5. todo, test, edit, test, commit
6. todo, test, edit, test, commit, tag

7. todo, branch, test, edit, test, commit, { tag, push, send patch }
8. todo, qnew, test, edit, test, commit, finish, { tag, push, send patch }



def test_true(): assert True == True
%logstart -o

!nosetests ./
!nosetests --help
!nosetests --ipdb   # pip install ipdbplugin # nose-progressive

# Run a file in IPython's namespace, print timing, skip sys.exit
%run -i -t -e ./

# Run a file in IPython's namespace, print timing, skip sys.exit, and pdb
%run -i -t -e -d ./

# Add >>> and ... prompts (useful for recording doctests)

# List defined aliases

# Get help for an alias

# List available aliases and commands

# Run a statement through the Python line profiler
%prun test_true()
%prun (2**2)*2)

# Time a number of loops
%timeit test_true()
%timeit (lambda x: (x**2)*2)(10)

# Run tests (see ipython_nose)


Python Logging Primer

A primer on Python logging (and testing):

#### Logging examples

# test setup (fixtures)
x = 2
data = {'x': x}

# asserts get compiled out with python -O2
assert x == 2

# these always run
class TestTwo(unittest.TestCase):
    def test_001_x_equals_2(self):
        self.assertEqual(self, x, 2)

if x != 2:
    ### Exceptions
    err = ('...', x)
    raise Exception(err)
    raise ValueError(err)

    ### Logging
    import logging
    log = logging                       # global logger
    log = __import__('logging')         # global logger
    log = logging.getLogger(__name__)   # local logger (<__name__>.py)
    # see: supervisor.loggers.LevelsByName and .LevelsByDescription
    TRAC = LevelsByName.TRAC = 5
    BLAT = LevelsByName.BLAT = 3
    log.log(err, TRAC)
    logging.addLevelName(TRAC, 'TRAC')
    logging.addLevelName(BLAT, 'BLAT')
    log.log(err, BLAT)  # not printed because msg.level < log.level

    log.log((err, 'string'), BLAT)

# run unittest.main() when run like:
#   python ./
# but not when imported like:
#   import filename
if __name__ == "__main__":
    import sys

See also:

Logging Strategies

Test Runners


nosetests --help
nosetests --with-xunit

# python -m pdb --help
nosetests --pdb
nosetests --pdb-failures
nosetests --pdb-errors

# pip install nose_ipdb
nosetests --ipdb --ipdb-failure

# pip install nose-progressive
nosetests --with-progressive


nose-ipdb is a Nose plugin for running pdb debugger with IPython (so tab completion, obj? and obj?? all work.


nose-parameterized is a Nose plugin for separating test data from test code.

See also:


ipython_nose is a Nose plugin for running test_* functions within the current Jupyter Notebook.

!pip install -e git+
%load_ext ipython_nose
def test_func():
    assert sum([1, 1]) == 2
%nose  # discover and run "test_*" functions


  • nosebook is a Nose plugin for running test functions in Jupyter Notebooks from the CLI.

nose and pdbpp

Nose and pdbpp:

pip install pdbpp
nosetests --pdb


Debugging: Console, CLI, Terminal, REPL, IPython


import ipdb; ipdb.set_trace()  # ipdb
nosetests --ipdb --ipdb-failure  # --with-progressive  # nose-progressive


pdb++ (pdbpp) is a “drop-in replacement for pdb” with additional debugging commands, tab completion, syntax highlighting, sticky mode,

  • When pdb++ is installed (in sys.path) pdb++ overloads pdb:
pip install pdbpp funcsigs  # installs
python -m pdb ./

nosetests --pdb ./
py.test --pdb ./

Performance Instrumentation and Profiling

Web Frameworks