Nose – TDD – Python

What, why

I’ve been reading up on TDD and it has struck me as particularly useful methodology to achieve “clean code that works”. TDD encourages writing unit tests to cover all the code (because by definition, you write a test before a line of code is written). Because all your code is covered you are freed from the fear of breakage due to change and can instantly be more confident and productive. Also, the test cases act as a specification in code – very useful.

Python has standard modules, unittest and doctest to help you write test cases. I simply love doctest. It alleviates much of the pain of writing a test case (setup and all) besides acting as “executable documentation”. The unittest module has a Java legacy and is not to my taste. Also, I wanted to find a solution that would help in automated test enumeration (discovery) in my source directories without having to write any “infrastructure” code. One more thing I was looking for was a way to run both unit tests and doc tests together.

After a bit of searching, I found “Nose“. Nose is a clone of “py.test” which I liked better than the original (subjectively). To get a feel of “Nose”, I set up some python test files.

The following is the directory structure and the contents of the files. I’ve put in both unit tests and doc tests in the files to see how “Nose” handles them. Also, the tests are spread across directories. Note that I had to put an “__init__.py” to allow “Nose” to import tests in a subdirectory.

The setup

The directory structure

prashanth@prashanth-desktop:~/tmp$ tree
.
|-- bingo.py
|-- somedir
|   |-- __init__.py
|   `-- test_another.py
`-- test_prashanth.py

1 directory, 4 files


bingo.py

def boing(a, b):
    '''
    >>> boing(10, 20)
    30
    '''
    return a+b

def boing1(a, b):
    '''
    >>> boing1(10, 20)
    40
    '''
    return a+b


test_prashanth.py

def test_a():
    assert 1

def test_b():
    print "hello"
    assert 0


somedir/test_another.py

def test_bingo():
    raise Exception('hgello')


Installing “Nose”

sudo easy_install nose


If you don’t have easy_install, head over here to get information on installation.

Running the tests

Now that “Nose” is installed, let us run the tests,

nosetests --with-doctest


The output is

..E.F
======================================================================
ERROR: somedir.test_another.test_bingo
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib/python2.5/site-packages/nose-0.10.2-py2.5.egg/nose/case.py", line 182, in runTest
    self.test(*self.arg)
  File "/home/prashanth/tmp/somedir/test_another.py", line 2, in test_bingo
    raise Exception('hgello')
Exception: hgello

======================================================================
FAIL: test_prashanth.test_b
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib/python2.5/site-packages/nose-0.10.2-py2.5.egg/nose/case.py", line 182, in runTest
    self.test(*self.arg)
  File "/home/prashanth/tmp/test_prashanth.py", line 7, in test_b
    assert 0
AssertionError:
-------------------- >> begin captured stdout << ---------------------
hello

--------------------- >> end captured stdout << ----------------------

----------------------------------------------------------------------
Ran 5 tests in 0.057s

FAILED (errors=1, failures=1)


The first line in the output is the "test progress" indication (..E.F) . When a test succeeds, a '.' is written. When a test fails, an 'F' is written. When a test throws an Exception, an 'E' is written. Very useful to get a sense of progress as a huge test suite being executed.

"Nose" captures the stdout and stderr when a test case fails to help you debug the issue. To learn more about using "Nose" go here.

No Trackbacks

2 Comments

  1. bhavesh

    TDD is something I heard when I first interacted with one of my Customers. Imagine a customer who came down, sat by your side and actually explained why he thinks TDD is something we should also incorporate in our development. He learnt it from his Oracle days and apparently is something that is widely followed in Oracle. Again insight I gained from my customer.

    We use a similar tool in our integration space – something I learnt to use 3 days back and still fine tuning it so that we can use in our projects. Nice to see a post on something I have been working on recently as well , but again our spaces are to different actually compare them ;-)

    Posted May 23, 2008 at 4:55 am | Permalink
  2. Yeah, TDD looks quite promising. However, there is substantially sized mental block to get started with it. I guess the only way to break it down is to grit my teeth and get down to work :) If you do start using TDD, do keep me posted about your experience.

    Posted May 23, 2008 at 12:21 pm | Permalink