This post has several examples, and covers fixtures, test discovery, asserts, running options, and running unittests and doctests.
Nose’s tagline is “nose extends unittest to make testing easier”.
It’s is a fairly well known python unit test framework, and can run doctests, unittests, and “no boilerplate” tests.
It is a good candidate for a go-to test framework.
I think a smart developer should get familiar doctest, unittest, pytest, and nose. Then decide if one of those makes the most sense for them, or if they want to keep looking for features only found in other frameworks.
That’s of course the reason why I’m writing this series. So I guess that last bit goes without saying.
Contents
- No boilerplate, some api
- Nose example
- Running nose
- Nose fixtures
- Testing markdown.py
- Nose assert_equals
- Test discovery
- Running unittests from nose
- Running doctests from nose
- More nose info (links)
- Examples on github
No boilerplate, some api
A basic test file for nose is pretty simple, without any boilerplate code, without required classes to drive from, without unnecessary imports, and without any extra api.
Note:
The module unnecessary_math is non-standard and can be found here: implementation of unnecessary_math.py
from unnecessary_math import multiply
def test_numbers_3_4():
assert multiply(3,4) == 12
This is identical to the simple test shown in my pytest intro.
There are differences between how you have to write your tests for the two frameworks once you get into extra features of the frameworks, like fixtures, plugins, assert mechanisms, etc.
I’m going to leave a full comparison of pytest and nose to a future post.
Why do I say ‘some api’? Well, when you get into fixtures (like setup/teardown, etc), there is some nose api that is needed in the tests. I’ll get into that in the fixture section.
Nose example
For completeness in following the styles of previous framework introductions, here is the full basic test.
This only differs from above that I’ve added another test function.
from unnecessary_math import multiply
def test_numbers_3_4():
assert multiply(3,4) == 12
def test_strings_a_3():
assert multiply('a',3) == 'aaa'
Running nose
To run nose, use the nosetests command that comes with nose.
nosetests test_um_nose.py
And with verbose:
nosetests -v test_um_nose.py
Here’s an example run both with and without verbose:
> nosetests test_um_nose.py
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
> nosetests -v test_um_nose.py
simple_example.test_um_nose.test_numbers_3_4 ... ok
simple_example.test_um_nose.test_strings_a_3 ... ok
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
Nose fixtures
Nose extends the unittest fixture model of setup/teardown.
We can add specific code to run:
-
at the beginning and end of a module of test code (setup_module/teardown_module)
To get this to work, you just have to use the right naming rules. -
at the beginning and end of a class of test methods (setup_class/teardown_class)
To get this to work, you have to use the right naming rules, and include the ‘@classmethod’ decorator. -
before and after a test function call (setup_function/teardown_function)
You can use any name. You have to apply them with the ‘@with_setup’ decorator imported from nose.
You can also use direct assignment, which I’ll show in the example. -
before and after a test method call (setup/teardown)
To get this to work, you have to use the right name.
The easiest fixtures to add are:
- setup_module() function: runs before anything else in the file
- teardown_module() function: runs after everything else in the file
And if you use a class to define some tests:
- setup() method: runs before every test method
- teardown() method: runs after every test method
You can also set non-class based test functions to have setup/teardown
functions, but you have to import the ‘with_setup’ decorator from nose, like so:
from nose import with_setup # optional
def my_setup_function():
pass
def my_teardown_function():
pass
@with_setup(my_setup_function, my_teardown_function)
def test_numbers_3_4():
assert multiply(3,4) == 12
If you don’t like to use decorators, you can also assign the setup and teardown attributes like this:
test_numbers_3_4.setup = my_setup_function
test_numbers_3_4.teardown = my_teardown_function
However, I think that’s a bit awkward.
With classes, you can set a setup/teardown for the class, but you do it differently.
You need to make sure the methods are class methods using the ‘classmethod’ decorator, and name them correctly, like so:
class TestUM:
@classmethod
def setup_class(cls):
print ("setup_class() before any methods in this class")
@classmethod
def teardown_class(cls):
print ("teardown_class() after any methods in this class")
It works, it’s just that you have to keep the syntax straight for all the different rules for different fixtures.
Here they are all together.
from nose import with_setup # optional
from unnecessary_math import multiply
def setup_module(module):
print ("") # this is to get a newline after the dots
print ("setup_module before anything in this file")
def teardown_module(module):
print ("teardown_module after everything in this file")
def my_setup_function():
print ("my_setup_function")
def my_teardown_function():
print ("my_teardown_function")
@with_setup(my_setup_function, my_teardown_function)
def test_numbers_3_4():
print 'test_numbers_3_4 <============================ actual test code'
assert multiply(3,4) == 12
@with_setup(my_setup_function, my_teardown_function)
def test_strings_a_3():
print 'test_strings_a_3 <============================ actual test code'
assert multiply('a',3) == 'aaa'
class TestUM:
def setup(self):
print ("TestUM:setup() before each test method")
def teardown(self):
print ("TestUM:teardown() after each test method")
@classmethod
def setup_class(cls):
print ("setup_class() before any methods in this class")
@classmethod
def teardown_class(cls):
print ("teardown_class() after any methods in this class")
def test_numbers_5_6(self):
print 'test_numbers_5_6() <============================ actual test code'
assert multiply(5,6) == 30
def test_strings_b_2(self):
print 'test_strings_b_2() <============================ actual test code'
assert multiply('b',2) == 'bb'
To see it in action, I’ll use the -s option, which turns off output capture.
This will show the order of the different fixture calls.
> nosetests -s test_um_nose_fixtures.py
....
setup_module before anything in this file
setup_class() before any methods in this class
TestUM:setup() before each test method
test_numbers_5_6() <============================ actual test code
TestUM:teardown() after each test method
TestUM:setup() before each test method
test_strings_b_2() <============================ actual test code
TestUM:teardown() after each test method
teardown_class() after any methods in this class
my_setup_function
test_numbers_3_4 <============================ actual test code
my_teardown_function
my_setup_function
test_strings_a_3 <============================ actual test code
my_teardown_function
teardown_module after everything in this file
----------------------------------------------------------------------
Ran 4 tests in 0.001s
OK
Testing markdown.py
This is also identical to code that can be run from py.test.
It’s similar to unittest code, but without boilerplate, and with simple assert calls instead of assertEquals.
Again, I’m using the API adapter to cleanly call markdown functionality.
Here’s the code to use nose to test markdown.py:
from markdown_adapter import run_markdown
def test_non_marked_lines():
print ('in test_non_marked_lines')
assert run_markdown('this line has no special handling') \
== 'this line has no special handling</p>'
def test_em():
print ('in test_em')
assert run_markdown('*this should be wrapped in em tags*') \
== '<p><em>this should be wrapped in em tags</em></p>'
def test_strong():
print ('in test_strong')
assert run_markdown('**this should be wrapped in strong tags**') \
== '<p><strong>this should be wrapped in strong tags</strong></p>'
And here’s the output:
> nosetests test_markdown_nose.py
FFF
======================================================================
FAIL: test_markdown_nose.test_non_marked_lines
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\python27\lib\site-packages\nose-1.2.1-py2.7.egg\nose\case.py", line 197, in runTest
self.test(*self.arg)
File "E:\python_notes\repo\markdown.py-dev\test_markdown_nose.py", line 13, in test_non_marked_lines
== 'this line has no special handling</p>'
AssertionError:
-------------------- >> begin captured stdout << ---------------------
in test_non_marked_lines
--------------------- >> end captured stdout << ----------------------
======================================================================
FAIL: test_markdown_nose.test_em
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\python27\lib\site-packages\nose-1.2.1-py2.7.egg\nose\case.py", line 197, in runTest
self.test(*self.arg)
File "E:\python_notes\repo\markdown.py-dev\test_markdown_nose.py", line 18, in test_em
== '<p><em>this should be wrapped in em tags</em></p>'
AssertionError:
-------------------- >> begin captured stdout << ---------------------
in test_em
--------------------- >> end captured stdout << ----------------------
======================================================================
FAIL: test_markdown_nose.test_strong
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\python27\lib\site-packages\nose-1.2.1-py2.7.egg\nose\case.py", line 197, in runTest
self.test(*self.arg)
File "E:\python_notes\repo\markdown.py-dev\test_markdown_nose.py", line 23, in test_strong
== '<p><strong>this should be wrapped in strong tags</strong></p>'
AssertionError:
-------------------- >> begin captured stdout << ---------------------
in test_strong
--------------------- >> end captured stdout << ----------------------
----------------------------------------------------------------------
Ran 3 tests in 0.137s
FAILED (failures=3)
All of the tests are failing.
Although the line numbers of the failures, along with the test function names, are printed, it’s not real obvious from the report what’s wrong.
Nose assert_equals
If we are using lots of assert something == somethingElse type tests, and we are committed to using nose for testing, we can use nose tools to make the report a bit obvious about what the failure is.
I’m going to rewrite the tests from above using nose.tools.assert_equals:
from nose.tools import assert_equals
from markdown_adapter import run_markdown
def test_non_marked_lines():
print ('in test_non_marked_lines')
assert_equals(run_markdown('this line has no special handling'),
'this line has no special handling</p>')
def test_em():
print ('in test_em')
assert_equals( run_markdown('*this should be wrapped in em tags*'),
'<p><em>this should be wrapped in em tags</em></p>')
def test_strong():
print ('in test_strong')
assert_equals( run_markdown('**this should be wrapped in strong tags**'),
'<p><strong>this should be wrapped in strong tags</strong></p>')
Nose’s assert_equals works a lot like unittest’s assertEquals.
Now lets look at the output:
> nosetests test_markdown_nose_assert_equals.py
FFF
======================================================================
FAIL: test_markdown_nose_assert_equals.test_non_marked_lines
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\python27\lib\site-packages\nose-1.2.1-py2.7.egg\nose\case.py", line 197, in runTest
self.test(*self.arg)
File "E:\python_notes\repo\markdown.py-dev\test_markdown_nose_assert_equals.py", line 14, in test_non_marked_lines
'this line has no special handling</p>')
AssertionError: 'this line has no special handling' != '<p>this line has no special handling</p>'
-------------------- >> begin captured stdout << ---------------------
in test_non_marked_lines
--------------------- >> end captured stdout << ----------------------
======================================================================
FAIL: test_markdown_nose_assert_equals.test_em
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\python27\lib\site-packages\nose-1.2.1-py2.7.egg\nose\case.py", line 197, in runTest
self.test(*self.arg)
File "E:\python_notes\repo\markdown.py-dev\test_markdown_nose_assert_equals.py", line 19, in test_em
'<p><em>this should be wrapped in em tags</em></p>')
AssertionError: '*this should be wrapped in em tags*' != '<p><em>this should be wrapped in em tags</em></p>'
-------------------- >> begin captured stdout << ---------------------
in test_em
--------------------- >> end captured stdout << ----------------------
======================================================================
FAIL: test_markdown_nose_assert_equals.test_strong
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\python27\lib\site-packages\nose-1.2.1-py2.7.egg\nose\case.py", line 197, in runTest
self.test(*self.arg)
File "E:\python_notes\repo\markdown.py-dev\test_markdown_nose_assert_equals.py", line 24, in test_strong
'<p><strong>this should be wrapped in strong tags</strong></p>')
AssertionError: '**this should be wrapped in strong tags**' != '<p><strong>this should be wrapped in strong tags</strong></p>'
-------------------- >> begin captured stdout << ---------------------
in test_strong
--------------------- >> end captured stdout << ----------------------
----------------------------------------------------------------------
Ran 3 tests in 0.139s
FAILED (failures=3)
Now the output makes it more obvious what’s wrong.
Test discovery
I use the same naming conventions for nose as I do for py.test.
- Name my test modules/files starting with ‘test_’.
- Name my test functions starting with ‘test_’.
- Name my test classes starting with ‘Test’.
- Name my test methods starting with ‘test_’.
- Make sure all packages with test code have an ‘init.py’ file.
These rules work just fine for me.
This isn’t the complete list of rules. If you want to do something different, look at the nose documentation for finding tests
Running unittests from nose
Nose finds and runs unittests with no problem, and with no extra steps.
Here I’ll run the tests from the unittest intro:
> nosetests test_um_unittest.py
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
> nosetests -v test_um_unittest.py
test_numbers_3_4 (simple_example.test_um_unittest.TestUM) ... ok
test_strings_a_3 (simple_example.test_um_unittest.TestUM) ... ok
----------------------------------------------------------------------
Ran 2 tests in 0.001s
OK
Running doctests from nose
Nose can run doctests, supposedly.
However, I couldn’t get it to work on doctests in a separate file method, using test_unnecessary_math.txt.
I tried several of the options, with no luck.
If you know what I’m doing wrong, please let me know.
More nose info (links)
- nose.readthedocs - nose official documentation (I think)
- pypi - download links and multiple versions
- nose-dev - google group on nose
Examples on github
All of the examples here are available in the markdown.py project on github.