Giter VIP home page Giter VIP logo

junitparser's Introduction

Hi there ๐Ÿ‘‹

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

Languages and Tools:

javascript python rust typescript

junitparser's People

Contributors

alde avatar aopincar avatar arewm avatar arichardson avatar bryan-hunt avatar carlescufi avatar cmarqu avatar codacy-badger avatar dependabot[bot] avatar enricomi avatar goblenus avatar jakub-polak avatar jkowalleck avatar kurtsansom avatar lukaszachy avatar markgras avatar me4502 avatar patbro avatar petterssonandreas avatar schancel avatar spredzy avatar steinheselmans avatar teake avatar tomwardill avatar wayjam avatar weiwei avatar yusijs avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

junitparser's Issues

Get properties of testcase

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>

JUnit report testsuites is not necessary

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

CLI `merge` output changed between version 1.4.2 and 1.5.0

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.

Why does junitparser.Attr HTML escape its value on read

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:

def __get__(self, instance, cls):
"""Gets value from attribute, return None if attribute doesn't exist."""
value = instance._elem.attrib.get(self.name)
if value is not None:
return escape(value)
return value

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 &amp; def != abc & def

Xml numbers parsed with system locale

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.

Writing to Element textcontent

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?

Add tests of the CLI

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.

Add types

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!

Error installing without binary packages

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 ?

Return XML as a variable

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).

JUnitXml.fromfile raises OSError if file is not found

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"

Parse broken JUnit schema created with PyTest test_id

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

New package 2.1.0 is broken

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.

How to access message of error / failure inside node ?

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 junitparser

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.

Failure to parse xUnit2

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.

Attributes with html-entities become decoded

For example, a testcase like:

<testcase name="Render with &quot;Dependencies&quot; 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

Typo in to_concole parameters

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.

Support python2 - open for contributions?

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.

Exclude tests from package

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.

pythonic way to create dict

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!

Installation fails with: ModuleNotFoundError: No module named 'future'

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.

Merging test files doesn't merge test counts

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.

Support different format flavors?

It appears that pytest has some extra attributes in its output. Maybe we can do

xml = JUnitXml.fromfile('/path/to/junit.xml', flavor='pytest')

Add support for "blocked" state

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>

Support rare Eclipse/JUnit 3 <testrun> ?

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

Support for getting any known data on an element

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.

lacking documentation on base classes

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 ๐Ÿ‘.

Error when importing IntAttr

I'm trying to import IntAttr to add a custom attribute, but python is complaining that ImportError: cannot import name 'IntAttr'.

Any ideas?

Unclosed file object in setup.py

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.

Documentation typo

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()

Add support for `junitparser merge` to print to `stdout`

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

Preserve / add testsuites.name when merging with CLI tool

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. :)

Junit XML with TestSuites vs TestSuite as root element

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.

TestCase might not have result

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

Parse errors with basic Jenkins results

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)

Parser fails if the tag `testsuites` is not present

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.

Project is not PEP 517 compliant

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.

system-out tag and system-err tag should be child of testsuite tag?

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>

`append` not working for custom Elements

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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.