I'm Weiwei Wang (็ๅทๅท). I live at Shanghai, China. I code and I translate.
- ๐ซ How to reach me: weiweiw.bsky.social
- ๐ฃ๏ธ I speak Chinese, English, and Spanish.
- ๐ Pronouns: He/him
- ๐ I write multilingual articles at weiwei.github.io
Parses JUnit/xUnit Result XML files with ease
Home Page: https://junitparser.readthedocs.io
License: Other
I'm Weiwei Wang (็ๅทๅท). I live at Shanghai, China. I code and I translate.
When I use googletest to export the test result, we add requirement numbers to the testcases with the function RecordProperty
.
How is it possible to get the property list of a testcase?
I see only testsuite has a properties() function.
This is a small example:
<?xml version="1.0" encoding="UTF-8"?>
<testsuites tests="1" failures="0" disabled="0" errors="0" timestamp="2020-02-27T10:30:10" time="1.218" name="AllTests">
<testsuite name="testSuite" tests="1" failures="0" disabled="0" errors="0" time="0.925">
<testcase name="test" status="run" time="0" classname="testSuite">
<properties>
<property name="req" value="requirement12"/>
</properties>
</testcase>
</testsuite>
</testsuites>
Looking at how fromFile
is implemented
The same if-else for xml file with and without testsuites should be implemented as well for fromstring
method
Otherwise, the fromstring
method will always produce empty test suites for a file without testsuites
tag
When read a junit report, which don't contain testsuites (only contain testsuite, it is also valid junit report), but it cannot work with this parser because it will check testsuites first
Suppose I have the following two JUnit XML files:
a.xml
<?xml version='1.0' encoding='utf-8'?>
<testsuite errors="0" failures="0" name="JUnit Jupiter" skipped="0" tests="2" time="0.041">
<testcase classname="a" name="a2()" time="0.015" />
<testcase classname="a" name="a1()" time="0.001" />
</testsuite>
b.xml
<?xml version='1.0' encoding='utf-8'?>
<testsuite errors="0" failures="0" name="JUnit Jupiter" skipped="0" tests="2" time="0.041">
<testcase classname="b" name="b1()" time="0.015" />
<testcase classname="b" name="b2()" time="0.001" />
</testsuite>
Then when I merge them with junitparser 1.5.0 cli, with the command junitparser merge a.xml b.xml c.xml
, I get the following output:
<?xml version='1.0' encoding='utf-8'?>
<testsuites errors="0" failures="0" skipped="0" tests="0" time="0">
<testcase classname="B" name="b1()" time="0.015" />
<testcase classname="B" name="b2()" time="0.001" />
</testsuites>
Note how only the cases of b.xml
are included, and also the name
of the <testsuite>
is lost.
If instead I swap the files to be merged (i.e. junitparser merge b.xml a.xml c.xml
) then only the cases of a.xml
are included.
But when I merge the files using Python instead of the command line, the two files do get merged as expected:
>>> import junitparser
>>> a = junitparser.JUnitXml.fromfile('a.xml')
>>> b = junitparser.JUnitXml.fromfile('b.xml')
>>> c = a + b
>>> c.write('c.xml')
>>> print(open('c.xml').read())
<?xml version='1.0' encoding='utf-8'?>
<testsuite errors="0" failures="0" name="JUnit Jupiter" skipped="0" tests="4" time="0.032">
<testcase classname="A" name="a1()" time="0.015" />
<testcase classname="A" name="a2()" time="0.001" />
<testcase classname="B" name="b1()" time="0.015" />
<testcase classname="B" name="b2()" time="0.001" />
</testsuite>
The files also get merged correctly when I use version 1.4.2 of the command line interface.
The Attr
class is used by classes that derive from Element
to provided easy access to common attributes. For instance, TestCase
has an Attr
called name
, which refers to the name
attribute of the respective <testcase>
element.
When I read (get) such an attribute, the string value is HTML escaped by calling into html.escape
:
junitparser/junitparser/junitparser.py
Lines 85 to 90 in c27bff2
What is the rational behind escaping the string value? Given junitparser provides access to JUnit data stored in XML files, I would expect the XML / HTML aspect being stripped away, so I retrieve pure text data.
Another contradiction is that writing an attribute (set) does not do the opposite operation. You would expect setting and then getting a value to retrieve the identical value that you have set:
from junitparser.junitparser import TestCase
testcase = TestCase()
testcase.name = 'abc & def'
assert testcase.name == 'abc & def'
The assertion fails:
abc & def != abc & def
Since version 2.0.0, junitparser uses the system's locale to parse times in the XML file, where it should use the file's locale. No matter on which system you parse the same JUnit file, it should always parse the correct number.
I presume, JUnit XML files always use en_US
or C
locale to format float numbers like the time. At least I would expect this to be the default locale to use to parse these files.
When I parse my files with system's locale de_DE
, I end up with wrong numbers. Running my tests with LC_ALL=C
, parsed times match the numbers in the JUnit file.
Hi. I am trying to emulate the pytest junit xml reports using your library.
So far it is going very well. However I am not able to find any option to write to the textcontent of an element.
For example, I have a custom element called stdin, and i would like to do something like:
<stdin>Something here</stdin>
I would like the same for Error:
<error message="Failed for reason">Something here</error>
Is this possible?
Hi, I stumbled across this while working on a project:
I think this is supposed to be if type_:
instead of if type:
, since now it's always true.
Kind regards,
Dries
Currently there are no tests of the command line interface usage of junitparser. They would be good to have to show potential problems after modifications. Eg. #78 was a case that might have been avoided by having tests covering the CLI usage.
Hey,
would you consider to add types to this package so that it can be checked with mypy and other type checkers?
Thanks a lot!
As part of CI testing for another project we install the resulting src and wheel packages separately without mixing them, for example
pip install --no-binary :all: dist/kiwitcms-junit.xml-plugin*.tar.gz
or pip install --only-binary :all: dist/kiwitcms_junit.xml_plugin*.whl
. Previously this used to work, however upgrading to junitparser 2.4.1 breaks it.
junitparser
is a direct dependency of the package under test.
Steps to reproduce:
Inside a fresh venv:
$ pip install -U pip future setuptools
Requirement already satisfied: pip in /tmp/xml/lib/python3.8/site-packages (21.3.1)
Requirement already satisfied: future in /tmp/xml/lib/python3.8/site-packages (0.18.2)
Requirement already satisfied: setuptools in /tmp/xml/lib/python3.8/site-packages (60.2.0)
$ pip freeze
future==0.18.2
$ pip install --no-binary :all: junitparser==2.4.1
Collecting junitparser==2.4.1
Using cached junitparser-2.4.1.tar.gz (12 kB)
Installing build dependencies ... done
Getting requirements to build wheel ... error
ERROR: Command errored out with exit status 1:
command: /tmp/xml/bin/python /tmp/xml/lib64/python3.8/site-packages/pip/_vendor/pep517/in_process/_in_process.py get_requires_for_build_wheel /tmp/tmpro0v4ray
cwd: /tmp/pip-install-_1yhp9kd/junitparser_8c82650a77c04649bbbaf69aae6be051
Complete output (16 lines):
Traceback (most recent call last):
File "/tmp/xml/lib64/python3.8/site-packages/pip/_vendor/pep517/in_process/_in_process.py", line 363, in <module>
main()
File "/tmp/xml/lib64/python3.8/site-packages/pip/_vendor/pep517/in_process/_in_process.py", line 345, in main
json_out['return_val'] = hook(**hook_input['kwargs'])
File "/tmp/xml/lib64/python3.8/site-packages/pip/_vendor/pep517/in_process/_in_process.py", line 130, in get_requires_for_build_wheel
return hook(config_settings)
File "/tmp/pip-build-env-qrj96qbf/overlay/lib/python3.8/site-packages/setuptools/build_meta.py", line 162, in get_requires_for_build_wheel
return self._get_build_requires(
File "/tmp/pip-build-env-qrj96qbf/overlay/lib/python3.8/site-packages/setuptools/build_meta.py", line 143, in _get_build_requires
self.run_setup()
File "/tmp/pip-build-env-qrj96qbf/overlay/lib/python3.8/site-packages/setuptools/build_meta.py", line 158, in run_setup
exec(compile(code, __file__, 'exec'), locals())
File "setup.py", line 4, in <module>
from junitparser import version
ModuleNotFoundError: No module named 'junitparser'
----------------------------------------
WARNING: Discarding https://files.pythonhosted.org/packages/d5/ac/4461949cc80116bed8d59746b28fdf0e19717f6b69e4d81af25763fb8da1/junitparser-2.4.1.tar.gz#sha256=9bf4f474d21621072d12c174012e5e79c599fa1758087680e96793786b1dc74c (from https://pypi.org/simple/junitparser/). Command errored out with exit status 1: /tmp/xml/bin/python /tmp/xml/lib64/python3.8/site-packages/pip/_vendor/pep517/in_process/_in_process.py get_requires_for_build_wheel /tmp/tmpro0v4ray Check the logs for full command output.
ERROR: Could not find a version that satisfies the requirement junitparser==2.4.1 (from versions: 0.1, 0.2, 0.3, 0.4, 0.5, 0.7, 0.8, 0.9, 1.0.0, 1.2.0, 1.2.1, 1.2.2, 1.3.0, 1.3.2, 1.3.3, 1.3.4, 1.4.0, 1.4.1, 1.4.2, 1.5.0, 1.5.1, 1.6.0, 1.6.1, 1.6.2, 1.6.3, 2.0.0b1, 2.0.0, 2.1.0, 2.1.1, 2.2.0, 2.3.0, 2.4.0, 2.4.1)
ERROR: No matching distribution found for junitparser==2.4.1
It is fb0b9f3 which triggers the failure but I think the reason is that setup.py
tries to import from junitparser
and for some reason this isn't found.
@weiwei my first question is what commands do you use to produce the tar.gz and wheel versions of this package ?
It would be nice if it was possible to get the XML as a string, not just to write it to a file (for example, for writing to stdout).
I suppose this behavior is inherited from LXML, but it still feels wrong -- one would expect a FileNotFoundError
.
Traceback (most recent call last):
File "my_code.py", line 35, in <module>
xml = JUnitXml.fromfile("results.xml")
File "/lib/python3.10/site-packages/junitparser/junitparser.py", line 322, in fromfile
tree = etree.parse(filepath) # nosec
File "src/lxml/etree.pyx", line 3521, in lxml.etree.parse
File "src/lxml/parser.pxi", line 1859, in lxml.etree._parseDocument
File "src/lxml/parser.pxi", line 1885, in lxml.etree._parseDocumentFromURL
File "src/lxml/parser.pxi", line 1789, in lxml.etree._parseDocFromFile
File "src/lxml/parser.pxi", line 1177, in lxml.etree._BaseParser._parseDocFromFile
File "src/lxml/parser.pxi", line 615, in lxml.etree._ParserContext._handleParseResultDoc
File "src/lxml/parser.pxi", line 725, in lxml.etree._handleParseResult
File "src/lxml/parser.pxi", line 652, in lxml.etree._raiseParseError
OSError: Error reading file 'results.xml': failed to load external entity "results.xml"
Hi,
we use PyTest to generate the JUnit reports with custom properties that break the JUnit schema because the properties are inserted into the testcase section, (xml header and testsuites omitted):
<testcase classname="test_function" file="test_function.py" line="0" name="test_function" time="0.0009"> <properties> <property name="test_id" value="1501" /> </properties> </testcase>
Could this be added to the package? I couldn't find a way to parse properties in test cases.
Kind Regards
Mario
Traceback (most recent call last):
File "/usr/local/bin/junitparser", line 8, in <module>
sys.exit(main())
File "/usr/local/lib/python3.7/site-packages/junitparser/cli.py", line 51, in main
args = args or _parser(prog_name=prog_name).parse_args()
File "/usr/local/lib/python3.7/site-packages/junitparser/cli.py", line 43, in _parser
"output", help="Merged XML Path, setting to " - " will output console"
TypeError: unsupported operand type(s) for -: 'str' and 'str'
78d2599#diff-1c9677d820a4b1a30ea84971c65cd4f12d3a49e4e4a0a52ea13b98541a1fef26R43
Line 43 is invalid.
Your module is really helpful. However I cannot get message of failures / errors from Junit XML field generated by Codeception,
This is XML produced by tests:
<testcase file="testcase.php" name="testname" class="class_name" assertions="13" time="64.285629">
<failure type="PHPUnit\Framework\ExpectationFailedException">Message of failure</failure>
</testcase>
<testcase file="testcase.php" name="testname2" class="class_name" assertions="0" time="61.843290">
<error type="Facebook\WebDriver\Exception\NoSuchElementException">Message of error</error>
</testcase>
and I cannot get messages Message of failure and Message of error inside XML tags. Can you please give me a hand here ?
junitparser -v
outputs the version of argparse
, not the version of junitparser
.
argparse.__version__
is currently equal to '1.1'
and was not changed many years (despite the changes in the module itself). It is planned for removing in future Python versions.
Hi,
The current junitparser is only compatible with xUnit v1 XML format which is deprecated in many places i.e. xUnit.net v1 XML Format
When attempting to parse junit.xml created with v2 format, it fails i.e. xUnit.net v2 XML Format
We are using as input the junit.xml file generated by pytest, starting from v6.0 their default is xunit2 schema
Please update your module to support the new format.
For example, a testcase like:
<testcase name="Render with "Dependencies" as title" time="0.222"> </testcase>
after passing through the junitparser it comes out as:
<testcase name="Render with "Dependencies" as title" time="0.222"> </testcase>
which isn't valid xml
In junitparser.py
, the JUnitXml.write
method as well as the write_xml
function have to_concole
parameters. I believe that this is a typo and that it is supposed to be to_console
.
For the scope of melexis/warnings-plugin#20, we are considering to use this junitparser for parsing failures from a JUnit xml file set.
I read that python2 is not supported in your readme. Would you be open to accept our contributions for supporting python2? Or is there a definite reason to not support it?
I couldn't find your email, so therefore this issue to ask the question.
As junitparser user I would like to not have tests
module in my enviroment. Currently installation of junitparser package install also tests
module e. g.:
> la venv/lib/python3.7/site-packages/ --blocks name
๏ __pycache__
๎ easy_install.py
๏ future
๏ future-0.18.2.dist-info
๏ junitparser
๏ junitparser-1.6.1.dist-info
๏ libfuturize
๏ libpasteurize
๏ past
๏ pip
๏ pip-19.2.3.dist-info
๏ pkg_resources
๏ setuptools
๏ setuptools-41.2.0.dist-info
๏ tests <-------------------------------------
It cause issues when I have my own tests
directory and run unittest - this tests
module is also discovered.
is there a simple way to get a python dict with all the information from the parsed xml? Eventually I want to dump is as a json, so it can be processed further.
Thanks for your amazing library!
I get a failure installing junitparser b/c the future
package isn't installed yet:
..... Trying to install the new tarball inside a virtualenv
Collecting setuptools
Using cached https://files.pythonhosted.org/packages/bf/ae/a23db1762646069742cc21393833577d3fa438eecaa59d11fb04fa57fcd5/setuptools-40.7.1-py2.py3-none-any.whl
Collecting pip
Using cached https://files.pythonhosted.org/packages/46/dc/7fd5df840efb3e56c8b4f768793a237ec4ee59891959d6a215d63f727023/pip-19.0.1-py2.py3-none-any.whl
Installing collected packages: setuptools, pip
Found existing installation: setuptools 28.8.0
Uninstalling setuptools-28.8.0:
Successfully uninstalled setuptools-28.8.0
Found existing installation: pip 9.0.1
Uninstalling pip-9.0.1:
Successfully uninstalled pip-9.0.1
Successfully installed pip-19.0.1 setuptools-40.7.1
Looking in links: dist/
Collecting kiwitcms-junit.xml-plugin
Collecting junitparser==1.2.2 (from kiwitcms-junit.xml-plugin)
Using cached https://files.pythonhosted.org/packages/77/17/1f2c12fc8aaba645c6f71b6a3b008aee2682b0179910bb258e7a6b5df547/junitparser-1.2.2.tar.gz
Complete output from command python setup.py egg_info:
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "/tmp/pip-install-shie07zw/junitparser/setup.py", line 4, in <module>
from junitparser import __version__
File "/tmp/pip-install-shie07zw/junitparser/junitparser/__init__.py", line 1, in <module>
from .junitparser import (
File "/tmp/pip-install-shie07zw/junitparser/junitparser/junitparser.py", line 12, in <module>
from future.utils import with_metaclass
ModuleNotFoundError: No module named 'future'
You can see the script which produces this at:
https://github.com/kiwitcms/junit.xml-plugin/blob/master/tests/bin/check-build - it builds packages in my repository and tries to install them in a local virtualenv before we push to PyPI.
While the junit xml file size is like 12MB, while parsing, there's an parse error.
Seems lxml should use iterparse method to parse large xml file.
Expect to fix this issue ASAP.
THanks
Some JUnit report producers support disabled
test cases.
For example, JUnit report format description here: https://llg.cubic.org/docs/junit/ has disabled
attribute on testsuites
and testsuite
tags. Would be nice to support that feature in junitparser
.
This is needed in context of EnricoMi/publish-unit-test-result-action#143
The description is: "Parases JUnit/xUnit Result XML files with ease"
s/Parases/Parses/g
I have two junit xml files. One for two different test suites run in parallel during my CI
I'm merging the resulting test xml files using junitparser to a single file in order to list the results in the CI
When I merge the files with the following testsuite results....
<testsuite errors="0" failures="0" name="pytest" skips="0" tests="16" time="43.390">
and
<testsuite errors="0" failures="0" name="pytest" skips="0" tests="14" time="15.434">
the resulting merged output is
<testsuites errors="0" failures="0" tests="0" time="0">
Note the plural 'testsuites', this does not contain any 'testsuite' elements.
Also note that the tests and time have not been summed as I would expect.
It appears that pytest has some extra attributes in its output. Maybe we can do
xml = JUnitXml.fromfile('/path/to/junit.xml', flavor='pytest')
I'm not sure whether Junit has a proper spec, but nevertheless, in our team we have reports with more than just pass-fail-skip, in particular, we do have "blocked" state. It would be great if junitparser could support it.
Example:
<testcase classname="mytest.type" name="test name" status="blocked"><blocked /></testcase>
If no <testsuites>
is present in the inputs, the merged output lost the name attribute of the <testsuite>
Hi, I'm in the process of porting junit2html over from its own internal Junit parser to your fantastic new one. Out of the input files in my tests I have one slightly unusual report that Junitparser won't parse.
I added support for it to junit2html from a bug report https://github.com/inorton/junit2html/issues/29 which is apparently a Junit report document produced by Eclipse that encapsulates test suites inside a <testrun>
tag.
I have a sample file here - https://gitlab.com/inorton/junit2html/-/blob/master/tests/junit-testrun.xml
Apparently it is a Junit 3 format - https://stackoverflow.com/questions/4165800/junit-test-timing-in-eclipse
Hello,
in Kiwi TCMS we are seeing failures parsing different XML files. The main difference is that some of them contain <testsuites>
tag while others don't, see:
kiwitcms/junit.xml-plugin#9
What would be a good way to discover the varying formats so we can adjust our parsing tools?
I am working with a testsuite that, when loaded, has data stored that is not directly available on an object's instance (i.e. number of test assertions). I was thinking about adding support to the Element
class to access any data in self._elem.attrib
with a getter (i.e. def get(self, attribute, default)
.
Would this functionality be desired? Is this the pythonic way of accessing the data?
I have no problem making the changes and submitting a PR myself.
I am trying to use this library to parse the results of our integration tests but there is basically no documentation on the base class(es) (Element). Some basic documentation on the properties of objects like TestSuite, TestCase and Result would be great. Nonetheless, great library ๐.
I'm trying to import IntAttr to add a custom attribute, but python is complaining that ImportError: cannot import name 'IntAttr'
.
Any ideas?
The read
function in setup.py
is implemented as such:
def read(fname):
try:
return open(os.path.join(os.path.dirname(__file__), fname)).read()
except IOError:
return ''
This function creates a file object via an open
call, but then does not close it. One should not depend on garbage collection to clean up resources. See the Python docs for details.
Changing the function like so fixes this issue:
def read(fname):
try:
with open(os.path.join(os.path.dirname(__file__), fname)) as f:
return f.read()
except IOError:
return ''
Alternatively, you can switch over to using setuptools
' declarative config, which can handle this for you.
Hi, I have corrected typos in your last example code.
Old:
from junitparser import Element, Attr, TestSuite
# Create the new element by subclassing Element or one of its child class,
# and add custom attributes to it.
class MyTestCase(TestCase):
foo = Attr()
Fixed:
from junitparser import TestCase, Attr, JUnitXml
# Create the new element by subclassing Element or one of its child class,
# and add custom attributes to it.
class MyTestCase(TestCase):
foo = Attr()
it would be nice for use of junitparser merge
in shell scripts (e.g. using pipes) if it could print to stdout
, for example by means of sing the pseudo-filename -
, a common Unix practice. This would remove the need to use a temporary intermediate file. I think this could done with argparse.FileType
.
a.xml
<?xml version='1.0' encoding='utf-8'?>
<testsuite errors="0" failures="0" name="JUnit" skipped="0" tests="1" time="0.0">
<testcase classname="a" name="a()" time="0.0" />
</testsuite>
b.xml
<?xml version='1.0' encoding='utf-8'?>
<testsuite errors="0" failures="0" name="JUnit" skipped="0" tests="1" time="0.0">
<testcase classname="b" name="b()" time="0.0" />
</testsuite>
What I expect:
$ junitparser merge a.xml b.xml -
<?xml version='1.0' encoding='utf-8'?>
<testsuites errors="0" failures="0" skipped="0" tests="2" time="0.0"><testsuite errors="0" failures="0" name="JUnit" skipped="0" tests="2" time="0.0"><testcase classname="a" name="a()" time="0.0" />
<testcase classname="b" name="b()" time="0.0" />
$ cat ./-
cat: ./-: No such file or directory
What happens:
$ junitparser merge a.xml b.xml -
$ cat ./-
<?xml version='1.0' encoding='utf-8'?>
<testsuites errors="0" failures="0" skipped="0" tests="2" time="0.0"><testsuite errors="0" failures="0" name="JUnit" skipped="0" tests="2" time="0.0"><testcase classname="a" name="a()" time="0.0" />
<testcase classname="b" name="b()" time="0.0" />
</testsuite></testsuites>
Final use case I have in mind (format merged XML with xmllint(1)
):
junitparser merge a.xml b.xml - | xmllint --format - > junit.xml
When running junitparser merge --glob *.xml junit-report.xml
where the files have <testsuites name=..">, the generated xml doesn't have the name-property for the testsuites tag. We're using this to group our tests, and we're having some issues with it.
I have found a couple workarounds which could work, but they are either messy/not stable (using sed to replace in the generated file) or more tedious to maintain (writing a custom python script where we append the name)
Would love a solution that could allow me to - in the cli - just run junitparser merge --glob *.xml junit-report.xml --suiteName "My Test Suite"
which seems like a fairly straight forward thing to add in cli.py? Another option could be --suiteAttributes or similar which takes in a comma-separated list to be more flexible:
junitparser merge --glob *.xml junit-report.xml --suiteAttributes "name=My Test Suite,custom=Custom Value"
I'm not super versed in python so I'm a bit concerned about messing around to much with this myself, but can always give it a stab if that would be better. :)
Hello,
As JUnit XML file inputs, I get sometimes TestSuite as root and sometimes TestSuites, which is as expected from the JUnit XSD file definition.
To be able to know in which case I'm, I do:
junitxml = JUnitXml.fromfile("junit-testsuites.xml")
# or junitxml = JUnitXml.fromfile("junit-testsuite.xml") without test suiteS element
# Analyse the type of the root element
instance_type = type(junitxml).__name__
Then depending on whether instance_type is JUnitXml or TestSuite, I'll parse differently.
Is this the proper way of dealing with the JUnit XML file root element variance when using junitparser module?
thanks.
Hi,
According to junit schema a test case have a sequence with skipped, error, failure, system-out and system-err with minimum occurrenses of 0.
When updating statistics this is causing the suite to have all skipped, error, failure values as 0 if the test cases does not have this value.
e.g:
<?xml version="1.0" encoding="UTF-8"?>
<testsuite name="com.example.suiteTest" time="0.147" tests="1" errors="1" skipped="0" failures="0">
<testcase name="createTest" classname="com.example.suiteTest" time="0.147"/>
</testsuite>
xml1 = JUnitXml.fromfile('example.xml')
print(xml1)
xml1.update_statistics()
print(xml1)
result:
<Element 'testsuite' errors="0" failures="1" name="com.example.suiteTest" skipped="0" tests="1" time="0.022">
<Element 'testsuite' errors="0" failures="0" name="com.example.suiteTest" skipped="0" tests="1" time="0.022">
the issue might be solved by adding an if condition in the update_statistics:
if self.errors > 0 and errors == 0:
errors = self.errors
if self.failures > 0 and failures == 0:
failures = self.failures
if self.skipped > 0 and skipped == 0:
skipped = self.skipped
I have been using your 2.0.0 beta release and have come across a problem when parsing some simple files such as this single report with one suite containing one test case. This is a genuine report from JUnit generate some time ago.
<?xml version="1.0" encoding="UTF-8"?>
<testsuite name="JUnitXmlReporter.constructor" errors="0" skipped="1" tests="3" failures="1" time="0.006" timestamp="2013-05-24T10:23:58">
<properties>
<property name="java.vendor" value="Sun Microsystems Inc." />
<property name="compiler.debug" value="on" />
<property name="project.jdk.classpath" value="jdk.classpath.1.6" />
</properties>
<testcase classname="JUnitXmlReporter.constructor" name="should default path to an empty string" time="0.1">
<failure message="test failure">Assertion failed</failure>
</testcase>
</testsuite>
I would expect to be able to iterate the suite and case easily eg:
import junitparser
xml = junitparser.JUnitXml.fromfile("tests/junit-simple_suite.xml")
for suite in xml:
print(suite.name)
for test in suite:
print(test.classname + " " + test.name)
However, The output I get is:
should default path to an empty string
Traceback (most recent call last):
File "<input>", line 4, in <module>
AttributeError: 'Failure' object has no attribute 'classname'
When I iterate the report instead of getting a TestSuite
I seem to be getting the first TestCase
(when it prints the suite name we get the name of the first test)
Taking as example the generated xml from nose2, and the snippet from the readme:
from junitparser import JUnitXml
xml = JUnitXml.fromfile('sample.xml')
for suite in xml:
print("Test suite name: {}".format(suite.name))
for case in suite:
print("Test case name: {}".format(case.name))
An exception is thrown:
Test suite name: test_gen:1
Traceback (most recent call last):
File "read_xml.py", line 8, in <module>
for case in suite:
TypeError: 'TestCase' object is not iterable
Expected result: The missing tag testsuites
should not affect the parsing or should be handled gracefully if not supported.
In order to be packaged into sdist and wheel distributables, this project implicitly requires the setuptools
and wheel
packages. PEP 517 basically says how a project should explicitly declare these dependencies via a pyproject.toml
file.
Closed.
I found system-out
tag and system-err
tag are under the testsuite
tag in my JUnit 4 report XML.
Is there any compatibility problem with all JUnit repot schema?
<?xml version="1.0" encoding="UTF-8"?>
<testsuite name="com.example.MyTest" tests="1" skipped="0" failures="0" errors="0" timestamp="2021-03-18T14:09:52" hostname="C02XR30SJG5D" time="6.03">
<properties/>
<testcase name="testMyCase" classname="com.example.MyTest" time="6.03"/>
<system-out><![CDATA[Downloading from maven
[Robolectric] com.example.MyTest: sdk=28; resources=LEGACY
[Robolectric] NOTICE: legacy resources mode is deprecated; see http://robolectric.org/migrating/#migrating-to-40
]]></system-out>
<system-err><![CDATA[[Robolectric] WARN: Android SDK 10000 requires Java 9 (have Java 8). Tests won't be run on SDK 10000 unless explicitly requested.
]]></system-err>
</testsuite>
I found that system-out
tag and system-err
tag are allowed to use in both testsuite
tag and testcase
tag according to junit5 xsd schema
here's the piece:
<xs:element name="testcase">
<xs:complexType>
<xs:sequence>
<xs:element ref="skipped" minOccurs="0" maxOccurs="1"/>
<xs:element ref="error" minOccurs="0" maxOccurs="unbounded"/>
<xs:element ref="failure" minOccurs="0" maxOccurs="unbounded"/>
<xs:element ref="system-out" minOccurs="0" maxOccurs="unbounded"/>
<xs:element ref="system-err" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="name" type="xs:string" use="required"/>
<xs:attribute name="assertions" type="xs:string" use="optional"/>
<xs:attribute name="time" type="xs:string" use="optional"/>
<xs:attribute name="classname" type="xs:string" use="optional"/>
<xs:attribute name="status" type="xs:string" use="optional"/>
</xs:complexType>
</xs:element>
<xs:element name="testsuite">
<xs:complexType>
<xs:sequence>
<xs:element ref="properties" minOccurs="0" maxOccurs="1"/>
<xs:element ref="testcase" minOccurs="0" maxOccurs="unbounded"/>
<xs:element ref="system-out" minOccurs="0" maxOccurs="1"/>
<xs:element ref="system-err" minOccurs="0" maxOccurs="1"/>
</xs:sequence>
<xs:attribute name="name" type="xs:string" use="required"/>
<xs:attribute name="tests" type="xs:string" use="required"/>
<xs:attribute name="failures" type="xs:string" use="optional"/>
<xs:attribute name="errors" type="xs:string" use="optional"/>
<xs:attribute name="time" type="xs:string" use="optional"/>
<xs:attribute name="disabled" type="xs:string" use="optional"/>
<xs:attribute name="skipped" type="xs:string" use="optional"/>
<xs:attribute name="timestamp" type="xs:string" use="optional"/>
<xs:attribute name="hostname" type="xs:string" use="optional"/>
<xs:attribute name="id" type="xs:string" use="optional"/>
<xs:attribute name="package" type="xs:string" use="optional"/>
</xs:complexType>
</xs:element>
The following code doesn't work. It doesn't throw any errors but it also doesn't append the Custom tag element.
from junitparser import Element, Attr, TestSuite
# Create the new element by subclassing Element,
# and add custom attributes to it.
class CustomElement(Element):
_tag = 'custom'
foo = Attr()
bar = Attr()
testcase = TestCase()
custom = CustomElement()
testcase.append(custom)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.