Nose has a bug - test names created by generators are not cached, so the error looks like it happened in the last test, not the actual test where it failed. I got around it following the solution in the bug report discussion, but it only works for names shown on stdout, not in the XML report (--with-xunit)
from functools import partial, update_wrapper
def testGenerator():
for i in range(10):
func = partial(test)
# make decorator with_setup() work again
update_wrapper(func, test)
func.description = "nice test name %s" % i
yield func
def test():
pass
The output of nose is as expected, something like
nice test name 0 ... ok
nice test name 1 ... ok
nice test name 2 ... ok
...
But the test names in XML are just 'testGenerator'.
...<testcase classname="example" name="testGenerator" time="0.000" />...
How can I change this so that the personalized test names are shown on both stdout and XML output?
I'm using nosetests version 1.1.2 and Python 2.6.6
You can change the way that Nose names tests by adding a plugin that implements describeTest
from nose.plugins import Plugin
class CustomName(Plugin):
"Change the printed description/name of the test."
def describeTest(self, test):
return "%s:%s" % (test.test.__module__, test.test.description)
You will then have to install this plugin, and enable it in the Nose invocation.
You can add the following line.
testGenerator.__name__ = "nice test name %s" % i
Example:
from functools import partial, update_wrapper
def testGenerator():
for i in range(10):
func = partial(test)
# make decorator with_setup() work again
update_wrapper(func, test)
func.description = "nice test name %s" % i
testGenerator.__name__ = "nice test name %s" % i
yield func
def test():
pass
This will result in the names you want.
<testsuite name="nosetests" tests="11" errors="0" failures="0" skip="0"><testcase classname="sample" name="nice test name 0" time="0.000" />
As Ananth mentions, you can use this.
testGenerator.__name__
You can also use this instead
testGenerator.compat_func_name
If your test class has arguments, I'd recommend currying them, as well as currying with_setup. Using lambda saves on the import, and I think it's a little cleaner. For example,
from nose.tools import with_setup
def testGenerator():
for i in range(10):
func = with_setup(set_up, tear_down)(lambda: test(i))
func.description = "nice test name %s" % i
testGenerator.compat_func_name = func.description
yield func
def test(i):
pass
def set_up():
pass
def tear_down():
pass
If using nose and Eclipe's PyUnit:
import nose
class Test(object):
CURRENT_TEST_NAME = None
def test_generator(self):
def the_test(*args,**kwargs):
pass
for i in range(10):
# Set test name
Test.CURRENT_TEST_NAME = "TestGenerated_%i"%i
the_test.description = Test.CURRENT_TEST_NAME
# Yield generated test
yield the_test,i
# Set the name of each test generated
test_generator.address = lambda arg=None:(__file__, Test, Test.CURRENT_TEST_NAME)
which will cause the name to show up nicely in PyUnit as well.