Python testing with nose by example

I believe I'm not the first developer who grew tired of all the setup required to make unit tests work with the unittest standard module. Luckily somebody decided to create nose.

Nose relies in healthy defaults rather than in explicit declarations. I#ll make this document example driven.

Installing nose

It's mainly a matter of getting the last version and making sure that the nosetests file is included in the PATH.

Examples

Here's the first example. Simply create a file and include a function whose name include the "test" token and run nose on them passing the file name as an argument. As you can see the test method was run and eq_ (similar to unittest's assertEquals) checked that it's two parameters were equal.

   1 #testset.py
   2 from nose.tools import eq_
   3 
   4 def test_sum():
   5     eq_(2+2,4)
   6 

C:\cesar\nosefiles>nosetests testset.py
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

In the following example I introduce a failing testing method. This time nose reports 2 test ran, one failed.

   1 #testset.py
   2 from nose.tools import ok_, eq_
   3 
   4 def test_sum():
   5     eq_(2+2,4)
   6 
   7 def test_failing_sum():
   8     ok_(2+2 == 3, "Expected failure")
   9 

C:\cesar\nosefiles>nosetests testset.py
.F
======================================================================
FAIL: testset.test_failing_sum
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Python26\lib\site-packages\nose-0.11.4-py2.6.egg\nose\case.py", line 186, in runTest
    self.test(*self.arg)
  File "C:\Documents and Settings\desales\Desktop\nose\testset.py", line 8, in test_failing_sum
    ok_(2+2 == 3, "Expected failure")
  File "C:\Python26\lib\site-packages\nose-0.11.4-py2.6.egg\nose\tools.py", line 25, in ok_
    assert expr, msg
AssertionError: Expected failure

----------------------------------------------------------------------
Ran 2 tests in 0.000s

FAILED (failures=1)

Let's suppose you made the test_failing_sum fail on purpose, perhaps because you are working on Test Driven Development. The nottest annotation will make nose ignore the decorated method. As a side note, the -v (verbose) flag will indicate nose to output the status for each test.

   1 #testset.py
   2 from nose.tools import ok_, eq_, nottest
   3 
   4 def test_sum():
   5     eq_(2+2,4)
   6 
   7 @nottest
   8 def test_failing_sum():
   9     ok_(2+2 == 3, "Expected failure")
  10 

C:\cesar\nosefiles>nosetests -v testset.py
testset.test_sum ... ok

----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

You can test classes as well. Please notice that in the following example the ignored method is not run by nose because it's not named after test. There's not even need to create an instance or a main method.

   1 from nose.tools import ok_, eq_
   2 
   3 def test_sum():
   4     eq_(2+2,4)
   5 
   6 def test_subs():
   7     ok_("Unexpected failure", 6-2 == 4)
   8 
   9 
  10 class TestSuite:
  11     def test_mult(self):
  12         eq_(2*2,4)
  13         
  14     def ignored(self):
  15         eq_(2*2,3)
  16 

C:\cesar\nosefiles>nosetests -v testset.py
testset.TestSuite.test_mult ... ok
testset.test_sum ... ok
testset.test_subs ... ok

----------------------------------------------------------------------
Ran 3 tests in 0.000s

OK

The istest decorator will mark a method as runnable by nose, even if it's name doesn't include the test substring:

   1 from nose.tools import ok_, eq_, istest
   2 
   3 def test_sum():
   4     eq_(2+2,4)
   5 
   6 def test_subs():
   7     ok_("Unexpected failure", 6-2 == 4)
   8 
   9 class TestSuite:
  10     def test_mult(self):
  11         eq_(2*2,4)
  12     
  13     @istest
  14     def ignored(self):
  15         eq_(2*2,4)
  16 

C:\cesar\nosefiles>nosetests -v testset.py
testset.TestSuite.ignored ... ok
testset.TestSuite.test_mult ... ok
testset.test_sum ... ok
testset.test_subs ... ok

----------------------------------------------------------------------
Ran 4 tests in 0.000s

OK

There's no need to have all of your tests in a single file. In the following example I have split the tests in two files and passed both names for nose to test. The same result can be achieved by running nose without passing any file name as argument, in which case all files named after test will be run by default.

   1 #test_set.py
   2 from nose.tools import ok_, eq_, istest
   3 
   4 def test_sum():
   5     eq_(2+2,4)
   6 
   7 def test_subs():
   8     ok_("Unexpected failure", 6-2 == 4)
   9 
  10 
  11 #complex_test_set.py
  12 from nose.tools import ok_, eq_, istest
  13 
  14 class TestSuite:
  15     def test_mult(self):
  16         eq_(2*2,4)
  17     
  18     @istest
  19     def ignored(self):
  20         eq_(2*2,4)
  21 

C:\cesar\nosefiles>nosetests -v test_set.py complex_test_set.py
complex_testset.TestSuite.ignored ... ok
complex_testset.TestSuite.test_mult ... ok
testset.test_sum ... ok
testset.test_subs ... ok

----------------------------------------------------------------------
Ran 4 tests in 0.016s

OK

C:\cesar\nosefiles>nosetests -v
complex_testset.TestSuite.ignored ... ok
complex_testset.TestSuite.test_mult ... ok
testset.test_sum ... ok
testset.test_subs ... ok

----------------------------------------------------------------------
Ran 4 tests in 0.016s

OK

Tests can also be configured in a file:

   1 #test_set.py
   2 from nose.tools import ok_, eq_, istest
   3 
   4 def test_sum():
   5     eq_(2+2,4)
   6 
   7 def test_subs():
   8     ok_("Unexpected failure", 6-2 == 4)
   9 

   1 #complex_test_set.py
   2 from nose.tools import ok_, eq_, istest
   3 
   4 class TestSuite:
   5     def test_mult(self):
   6         eq_(2*2,4)
   7     
   8     def ignored(self):
   9         eq_(2*2,4)
  10 

#config.ini
[nosetests]

where=C:\cesar\nosefiles

#test only some modules
tests=testset.py,
      complex_testset.py
      
      
C:\cesar\nosefiles>nosetests -s -v -c config.ini
testset.test_sum ... ok
testset.test_subs ... ok
complex_testset.TestSuite.ignored ... ok
complex_testset.TestSuite.test_mult ... ok

----------------------------------------------------------------------
Ran 4 tests in 0.016s

OK

There's even the possibility to specify which functions/methods will be run, both in the command line or in a configuration file:

   1 #test_set.py
   2 from nose.tools import ok_, eq_, istest, nottest
   3 
   4 def test_sum():
   5     eq_(2+2,4)
   6 
   7 def test_subs():
   8     ok_("Unexpected failure", 6-2 == 4)
   9 

   1 #complex_test_set.py
   2 from nose.tools import ok_, eq_, istest, nottest
   3 
   4 class TestSuite:
   5     def test_mult(self):
   6         eq_(2*2,4)
   7     
   8     def ignored(self):
   9         eq_(2*2,4)
  10 

#config.ini
[nosetests]

where=C:\cesar\nosefiles

#test only some modules
tests=testset.py:test_sum,
      complex_testset.py:TestSuite.ignored

C:\cesar\nosefiles>nosetests -s -v -c config.ini
testset.test_sum ... ok
complex_testset.TestSuite.ignored ... ok

----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK

All updates to this tutorial will be posted in my blog

Created by Cesar C. Desales, November 2011.

PythonNoseTesting (last edited 2011-12-16 08:21:34 by ErikRivera)