Outputting XML Test Reports
Note
New in version 0.2
Output test reports in junit-xml format.
This plugin implements startTest()
, testOutcome()
and
stopTestRun()
to compile and then output a test report in
junit-xml format. By default, the report is written to a file called
nose2-junit.xml
in the current working directory.
You can configure the output filename by setting path
in a [junit-xml]
section in a config file. Unicode characters which are invalid in XML 1.0
are replaced with the U+FFFD
replacement character. In the case that your
software throws an error with an invalid byte string.
By default, the ranges of discouraged characters are replaced as well. This can
be changed by setting the keep_restricted
configuration variable to
True
.
By default, the arguments of parametrized and generated tests are not printed. For instance, the following code:
# a.py
from nose2 import tools
def test_gen():
def check(a, b):
assert a == b, '{}!={}'.format(a,b)
yield check, 99, 99
yield check, -1, -1
@tools.params('foo', 'bar')
def test_params(arg):
assert arg in ['foo', 'bar', 'baz']
Produces this XML by default:
<testcase classname="a" name="test_gen:1" time="0.000171"
timestamp="2021-12-09T21:28:09.686611">
<system-out />
</testcase>
<testcase classname="a" name="test_gen:2" time="0.000202"
timestamp="2021-12-09T21:28:09.686813">
<system-out />
</testcase>
<testcase classname="a" name="test_params:1" time="0.000159"
timestamp="2021-12-09T21:28:09.686972">
<system-out />
</testcase>
<testcase classname="a" name="test_params:2" time="0.000163"
timestamp="2021-12-09T21:28:09.687135">
<system-out />
</testcase>
But if test_fullname
is True
, then the following XML is
produced:
<testcase classname="a" name="test_gen:1 (99, 99)" time="0.000213"
timestamp="2021-12-09T21:28:09.686611">
<system-out />
</testcase>
<testcase classname="a" name="test_gen:2 (-1, -1)" time="0.000194"
timestamp="2021-12-09T21:28:09.687105">
<system-out />
</testcase>
<testcase classname="a" name="test_params:1 ('foo')" time="0.000178"
timestamp="2021-12-09T21:28:09.687283">
<system-out />
</testcase>
<testcase classname="a" name="test_params:2 ('bar')" time="0.000187"
timestamp="2021-12-09T21:28:09.687470">
<system-out />
</testcase>
Enable this Plugin
This plugin is built-in, but not loaded by default.
Even if you specify always-on = True
in the configuration, it will not run unless you also enable it. You can do so by putting the following in a unittest.cfg
or nose2.cfg
file
[unittest]
plugins = nose2.plugins.junitxml
The plugins
parameter may contain a list of plugin names, including nose2.plugins.junitxml
Configuration [junit-xml]
- always-on
- Default
False
- Type
boolean
- keep_restricted
- Default
False
- Type
boolean
- path
- Default
nose2-junit.xml
- Type
str
- test_fullname
- Default
False
- Type
boolean
- test_properties
- Default
None
- Type
str
Sample configuration
The default configuration is equivalent to including the following in a unittest.cfg
file.
[junit-xml]
always-on = False
keep_restricted = False
path = nose2-junit.xml
test_fullname = False
Command-line options
- --junit-xml-path FILE
Output XML filename
- -X DEFAULT, --junit-xml DEFAULT
Generate junit-xml output report
Plugin class reference: JUnitXmlReporter
Sample output
The XML test report for nose2’s sample scenario with tests in a package looks like this:
<testsuite errors="1" failures="5" name="nose2-junit" skips="1" tests="25" time="0.004">
<testcase classname="pkg1.test.test_things" name="test_gen:1" time="0.000141" />
<testcase classname="pkg1.test.test_things" name="test_gen:2" time="0.000093" />
<testcase classname="pkg1.test.test_things" name="test_gen:3" time="0.000086" />
<testcase classname="pkg1.test.test_things" name="test_gen:4" time="0.000086" />
<testcase classname="pkg1.test.test_things" name="test_gen:5" time="0.000087" />
<testcase classname="pkg1.test.test_things" name="test_gen_nose_style:1" time="0.000085" />
<testcase classname="pkg1.test.test_things" name="test_gen_nose_style:2" time="0.000090" />
<testcase classname="pkg1.test.test_things" name="test_gen_nose_style:3" time="0.000085" />
<testcase classname="pkg1.test.test_things" name="test_gen_nose_style:4" time="0.000087" />
<testcase classname="pkg1.test.test_things" name="test_gen_nose_style:5" time="0.000086" />
<testcase classname="pkg1.test.test_things" name="test_params_func:1" time="0.000093" />
<testcase classname="pkg1.test.test_things" name="test_params_func:2" time="0.000098">
<failure message="test failure">Traceback (most recent call last):
File "nose2/plugins/loader/parameters.py", line 162, in func
return obj(*argSet)
File "nose2/tests/functional/support/scenario/tests_in_package/pkg1/test/test_things.py", line 64, in test_params_func
assert a == 1
AssertionError
</failure>
</testcase>
<testcase classname="pkg1.test.test_things" name="test_params_func_multi_arg:1" time="0.000094" />
<testcase classname="pkg1.test.test_things" name="test_params_func_multi_arg:2" time="0.000089">
<failure message="test failure">Traceback (most recent call last):
File "nose2/plugins/loader/parameters.py", line 162, in func
return obj(*argSet)
File "nose2/tests/functional/support/scenario/tests_in_package/pkg1/test/test_things.py", line 69, in test_params_func_multi_arg
assert a == b
AssertionError
</failure>
</testcase>
<testcase classname="pkg1.test.test_things" name="test_params_func_multi_arg:3" time="0.000096" />
<testcase classname="" name="test_fixt" time="0.000091" />
<testcase classname="" name="test_func" time="0.000084" />
<testcase classname="pkg1.test.test_things.SomeTests" name="test_failed" time="0.000113">
<failure message="test failure">Traceback (most recent call last):
File "nose2/tests/functional/support/scenario/tests_in_package/pkg1/test/test_things.py", line 17, in test_failed
assert False, "I failed"
AssertionError: I failed
</failure>
</testcase>
<testcase classname="pkg1.test.test_things.SomeTests" name="test_ok" time="0.000093" />
<testcase classname="pkg1.test.test_things.SomeTests" name="test_params_method:1" time="0.000099" />
<testcase classname="pkg1.test.test_things.SomeTests" name="test_params_method:2" time="0.000101">
<failure message="test failure">Traceback (most recent call last):
File "nose2/plugins/loader/parameters.py", line 144, in _method
return method(self, *argSet)
File "nose2/tests/functional/support/scenario/tests_in_package/pkg1/test/test_things.py", line 29, in test_params_method
self.assertEqual(a, 1)
AssertionError: 2 != 1
</failure>
</testcase>
<testcase classname="pkg1.test.test_things.SomeTests" name="test_skippy" time="0.000104">
<skipped />
</testcase>
<testcase classname="pkg1.test.test_things.SomeTests" name="test_typeerr" time="0.000096">
<error message="test failure">Traceback (most recent call last):
File "nose2/tests/functional/support/scenario/tests_in_package/pkg1/test/test_things.py", line 13, in test_typeerr
raise TypeError("oops")
TypeError: oops
</error>
</testcase>
<testcase classname="pkg1.test.test_things.SomeTests" name="test_gen_method:1" time="0.000094" />
<testcase classname="pkg1.test.test_things.SomeTests" name="test_gen_method:2" time="0.000090">
<failure message="test failure">Traceback (most recent call last):
File "nose2/plugins/loader/generators.py", line 145, in method
return func(*args)
File "nose2/tests/functional/support/scenario/tests_in_package/pkg1/test/test_things.py", line 24, in check
assert x == 1
AssertionError
</failure>
</testcase>
</testsuite>