Tagging allows you to organize and run subsets of your tests. As I mentioned before in the section on the differences between UnitTests and nose-style tests, there are some subtle differences between the two methods, and test organization is one of them. Nose provides a very simple and robust tagging system that allows you to easily cherry-pick the tests you want to run.
Let's say we have the following four tests:
This will be 'test_one_fish.py:'
def test_one_fish(): print "I'm the one fish test." test_one_fish.tags = ["number", "one"]
This will be 'test_two_fish.py:'
def test_two_fish(): print "I'm the two fish test." test_two_fish.tags = ["number", "two"]
This will be 'test_red_fish.py:'
def test_red_fish(): print "I'm the red fish test." test_red_fish.tags = ["color", "red"]
This will be 'test_blue_fish.py:'
def test_blue_fish(): print "I'm the blue fish test." test_blue_fish.tags = ["color", "blue"]
You'll notice that in each test case, the test function itself has a tags attribute that's a list of strings. We can use that attribute to tell nose that we only want to run tests that have a given tag. Let's start by running them all; this will look familiar:
[terryp@tpmacbook] sample_tags :: nosetests .... ---------------------------------------------------------------------- Ran 4 tests in 0.008s OK
Let's turn on a little verbosity:
[terryp@tpmacbook] sample_tags :: nosetests -v test_blue_fish.test_blue_fish ... ok test_one_fish.test_one_fish ... ok test_red_fish.test_red_fish ... ok test_two_fish.test_two_fish ... ok ---------------------------------------------------------------------- Ran 4 tests in 0.007s OK
Okay, now let's get tricky. Let's only run tests that we tagged as 'numbers.' If we tagged everything correctly, that'll be 'test_one_fish.py' and 'test_two_fish.py.' The easiest way to do this with nose is by using the -a flag ('a' is for 'attribute'):
[terryp@tpmacbook] sample_tags :: nosetests -v -a tags=number test_one_fish.test_one_fish ... ok test_two_fish.test_two_fish ... ok ---------------------------------------------------------------------- Ran 2 tests in 0.006s OK
We can do the same thing if we want to run the tests that have colors. That should be 'test_red_fish.py' and 'test_blue_fish.py':
[terryp@tpmacbook] sample_tags :: nosetests -v -a tags=color test_blue_fish.test_blue_fish ... ok test_red_fish.test_red_fish ... ok ---------------------------------------------------------------------- Ran 2 tests in 0.007s OK
There are other ways to tag tests, too. You can add one line to each test module for each tag you want to use:
def test_blue_fish(): print "I'm the blue fish test." test_blue_fish.color = True test_blue_fish.blue = True
This has the advantage of letting you use tags as negative descriptors, but obviously it results in a lot of boilerplate code if you're using a lot of tags. The way to get around that is to write a simple decorator function:
def tag(*tags): def wrap(func): for tag in tags: setattr(func, tag, True) return func return wrap
Now you can do this:
@tag('color', 'blue') def test_blue_fish(): print "I'm the blue fish test."
Which, in turn, allows you to do this:
[terryp@tpmacbook] sample_tags :: nosetests -v -a '!color' test_blue_fish.test_one_fish ... ok test_red_fish.test_two_fish ... ok ---------------------------------------------------------------------- Ran 2 tests in 0.007s OK
You just ran the tests that you did not have tagged with 'color.'
A few things to note about this approach. First, because you're setting the tags themselves as attributes, you run them with simply nosetests -a $tagname, instead of with nosetests -a tags=$tagname. Second, to use them negatively, you have to use single quotes (otherwise the shell will interpret that !). Third, if you combine the tagging decorator with test generators, be sure to put the decorator on the generator, and not the yielded function.
And there you have it: tagging, nose-style.
Fluid 960 Grid System, created by Stephen Bau, based on the 960 Grid System by Nathan Smith. Released under the GPL/ MIT Licenses.