junitparser


Namejunitparser JSON
Version 3.2.0 PyPI version JSON
download
home_pagehttps://github.com/weiwei/junitparser
SummaryManipulates JUnit/xUnit Result XML files
upload_time2024-09-01 04:07:42
maintainerNone
docs_urlNone
authorWeiwei Wang
requires_pythonNone
licenseApache 2.0
keywords junit xunit xml parser
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage
            junitparser -- Pythonic JUnit/xUnit Result XML Parser
======================================================

.. image:: https://github.com/weiwei/junitparser/workflows/build/badge.svg?branch=master
   :target: https://github.com/weiwei/junitparser/actions
.. image:: https://codecov.io/gh/weiwei/junitparser/branch/master/graph/badge.svg?token=UotlfRXNnK
   :target: https://codecov.io/gh/weiwei/junitparser

junitparser handles JUnit/xUnit Result XML files. Use it to parse and manipulate
existing Result XML files, or create new JUnit/xUnit result XMLs from scratch.

Features
--------

* Parse or modify existing JUnit/xUnit XML files.
* Parse or modify non-standard or customized JUnit/xUnit XML files, by monkey
  patching existing element definitions.
* Create JUnit/xUnit test results from scratch.
* Merge test result XML files.
* Specify XML parser. For example you can use lxml to speed things up.
* Invoke from command line, or `python -m junitparser`
* Python 2 and 3 support (As of Nov 2020, 1/4 of the users are still on Python
  2, so there is no plan to drop Python 2 support)

Note on version 2
-----------------

Version 2 improved support for pytest result XML files by fixing a few issues,
notably that there could be multiple <Failure> or <Error> entries. There is a
breaking change that ``TestCase.result`` is now a list instead of a single item.
If you are using this attribute, please update your code accordingly.

Installation
-------------

::

    pip install junitparser

Usage
-----

You should be relatively familiar with the Junit XML format. If not, run
``pydoc`` on the exposed classes and functions to see how it's structured.

Create Junit XML format reports from scratch
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

You have some test result data, and you want to convert them into junit.xml
format.

.. code-block:: python

    from junitparser import TestCase, TestSuite, JUnitXml, Skipped, Error

    # Create cases
    case1 = TestCase('case1', 'class.name', 0.5) # params are optional
    case1.classname = "modified.class.name" # specify or change case attrs
    case1.result = [Skipped()] # You can have a list of results
    case2 = TestCase('case2')
    case2.result = [Error('Example error message', 'the_error_type')]

    # Create suite and add cases
    suite = TestSuite('suite1')
    suite.add_property('build', '55')
    suite.add_testcase(case1)
    suite.add_testcase(case2)
    suite.remove_testcase(case2)

    #Bulk add cases to suite
    case3 = TestCase('case3')
    case4 = TestCase('case4')
    suite.add_testcases([case3, case4])

    # Add suite to JunitXml
    xml = JUnitXml()
    xml.add_testsuite(suite)
    xml.write('junit.xml')

Read and manipulate existing JUnit/xUnit XML files
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

You have some existing junit.xml files, and you want to modify the content.

.. code-block:: python

    from junitparser import JUnitXml

    xml = JUnitXml.fromfile('/path/to/junit.xml')
    for suite in xml:
        # handle suites
        for case in suite:
            # handle cases
    xml.write() # Writes back to file

It is also possible to use a custom parser. For example lxml provides a plethora
of parsing options_. We can use them this way:

.. code-block:: python

    from lxml.etree import XMLParser, parse
    from junitparser import JUnitXml

    def parse_func(file_path):
        xml_parser = XMLParser(huge_tree=True)
        return parse(file_path, xml_parser)

    xml = JUnitXml.fromfile('/path/to/junit.xml', parse_func)
    # process xml...

.. _options: https://lxml.de/api/lxml.etree.XMLParser-class.html

Merge XML files
~~~~~~~~~~~~~~~

You have two or more XML files, and you want to merge them into one.

.. code-block:: python

    from junitparser import JUnitXml

    xml1 = JUnitXml.fromfile('/path/to/junit1.xml')
    xml2 = JUnitXml.fromfile('/path/to/junit2.xml')

    newxml = xml1 + xml2
    # Alternatively, merge in place
    xml1 += xml2

Note that it won't check for duplicate entries. You need to deal with them on
your own.

Schema Support
~~~~~~~~~~~~~~~

By default junitparser supports the schema of windyroad_, which is a relatively
simple schema.

.. _windyroad: https://github.com/windyroad/JUnit-Schema/blob/master/JUnit.xsd

Junitparser also support extra schemas:

.. code-block:: python

    from junitparser.xunit2 import JUnitParser, TestCase, TestSuite, \
        RerunFailure
    # These classes are redefined to support extra properties and attributes
    # of the xunit2 schema.
    suite = TestSuite("mySuite")
    suite.system_err = "System err" # xunit2 specific property
    case = TestCase("myCase")
    rerun_failure = RerunFailure("Not found", "404") # case property
    rerun_failure.stack_trace = "Stack"
    rerun_failure.system_err = "E404"
    rerun_failure.system_out = "NOT FOUND"
    case.add_rerun_result(rerun_failure)

Currently supported schemas including:

- xunit2_, supported by pytest, Erlang/OTP, Maven Surefire, CppTest, etc.

.. _xunit2: https://github.com/jenkinsci/xunit-plugin/blob/xunit-2.3.2/src/main/resources/org/jenkinsci/plugins/xunit/types/model/xsd/junit-10.xsd

PRs are welcome to support more schemas.

Create XML with custom attributes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

You want to use an attribute that is not supported by default.

.. code-block:: python

    from junitparser import TestCase, Attr, IntAttr, FloatAttr

    # Add the custom attribute
    TestCase.id = IntAttr('id')
    TestCase.rate = FloatAttr('rate')
    TestCase.custom = Attr('custom')
    case = TestCase()
    case.id = 123
    case.rate = 0.95
    case.custom = 'foobar'


Handling XML with custom element
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

There may be once in 1000 years you want to it this way, but anyways.
Suppose you want to add element CustomElement to TestCase.

.. code-block:: python

    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)
    # To find a single sub-element:
    testcase.child(CustomElement)
    # To iterate over custom elements:
    for custom in testcase.iterchildren(CustomElement):
        ... # Do things with custom element

Handling custom XML attributes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Say you have some data stored in the XML as custom attributes and you want to
read them out:

.. code-block:: python

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

    xml = JUnitXml.fromfile('/path/to/junit.xml')
    for suite in xml:
        # handle suites
        for case in suite:
            my_case = MyTestCase.fromelem(case)
            print(my_case.foo)

Command Line
------------

.. code-block:: console

    $ junitparser --help
    usage: junitparser [-h] [-v] {merge} ...

    Junitparser CLI helper.

    positional arguments:
    {merge}        command
      merge        Merge Junit XML format reports with junitparser.
      verify       Return a non-zero exit code if one of the testcases failed or errored.

    optional arguments:
    -h, --help     show this help message and exit
    -v, --version  show program's version number and exit


.. code-block:: console

    $ junitparser merge --help
    usage: junitparser merge [-h] [--glob] paths [paths ...] output

    positional arguments:
      paths       Original XML path(s).
      output      Merged XML Path, setting to "-" will output console

    optional arguments:
      -h, --help  show this help message and exit
      --glob      Treat original XML path(s) as glob(s).
      --suite-name SUITE_NAME
                  Name added to <testsuites>.

.. code-block:: console

    $ junitparser verify --help
    usage: junitparser verify [-h] [--glob] paths [paths ...]

    positional arguments:
      paths       XML path(s) of reports to verify.

    optional arguments:
      -h, --help  show this help message and exit
      --glob      Treat original XML path(s) as glob(s).

Test
----

The tests are written with python ``unittest``, to run them, use pytest::

    pytest test.py

Contribute
----------

PRs are welcome!

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/weiwei/junitparser",
    "name": "junitparser",
    "maintainer": null,
    "docs_url": null,
    "requires_python": null,
    "maintainer_email": null,
    "keywords": "junit xunit xml parser",
    "author": "Weiwei Wang",
    "author_email": "gastlygem@gmail.com",
    "download_url": "https://files.pythonhosted.org/packages/57/88/6a268028a297751ed73be8e291f12aa727caf22adbc218e8dfbafcc974af/junitparser-3.2.0.tar.gz",
    "platform": null,
    "description": "junitparser -- Pythonic JUnit/xUnit Result XML Parser\n======================================================\n\n.. image:: https://github.com/weiwei/junitparser/workflows/build/badge.svg?branch=master\n   :target: https://github.com/weiwei/junitparser/actions\n.. image:: https://codecov.io/gh/weiwei/junitparser/branch/master/graph/badge.svg?token=UotlfRXNnK\n   :target: https://codecov.io/gh/weiwei/junitparser\n\njunitparser handles JUnit/xUnit Result XML files. Use it to parse and manipulate\nexisting Result XML files, or create new JUnit/xUnit result XMLs from scratch.\n\nFeatures\n--------\n\n* Parse or modify existing JUnit/xUnit XML files.\n* Parse or modify non-standard or customized JUnit/xUnit XML files, by monkey\n  patching existing element definitions.\n* Create JUnit/xUnit test results from scratch.\n* Merge test result XML files.\n* Specify XML parser. For example you can use lxml to speed things up.\n* Invoke from command line, or `python -m junitparser`\n* Python 2 and 3 support (As of Nov 2020, 1/4 of the users are still on Python\n  2, so there is no plan to drop Python 2 support)\n\nNote on version 2\n-----------------\n\nVersion 2 improved support for pytest result XML files by fixing a few issues,\nnotably that there could be multiple <Failure> or <Error> entries. There is a\nbreaking change that ``TestCase.result`` is now a list instead of a single item.\nIf you are using this attribute, please update your code accordingly.\n\nInstallation\n-------------\n\n::\n\n    pip install junitparser\n\nUsage\n-----\n\nYou should be relatively familiar with the Junit XML format. If not, run\n``pydoc`` on the exposed classes and functions to see how it's structured.\n\nCreate Junit XML format reports from scratch\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nYou have some test result data, and you want to convert them into junit.xml\nformat.\n\n.. code-block:: python\n\n    from junitparser import TestCase, TestSuite, JUnitXml, Skipped, Error\n\n    # Create cases\n    case1 = TestCase('case1', 'class.name', 0.5) # params are optional\n    case1.classname = \"modified.class.name\" # specify or change case attrs\n    case1.result = [Skipped()] # You can have a list of results\n    case2 = TestCase('case2')\n    case2.result = [Error('Example error message', 'the_error_type')]\n\n    # Create suite and add cases\n    suite = TestSuite('suite1')\n    suite.add_property('build', '55')\n    suite.add_testcase(case1)\n    suite.add_testcase(case2)\n    suite.remove_testcase(case2)\n\n    #Bulk add cases to suite\n    case3 = TestCase('case3')\n    case4 = TestCase('case4')\n    suite.add_testcases([case3, case4])\n\n    # Add suite to JunitXml\n    xml = JUnitXml()\n    xml.add_testsuite(suite)\n    xml.write('junit.xml')\n\nRead and manipulate existing JUnit/xUnit XML files\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nYou have some existing junit.xml files, and you want to modify the content.\n\n.. code-block:: python\n\n    from junitparser import JUnitXml\n\n    xml = JUnitXml.fromfile('/path/to/junit.xml')\n    for suite in xml:\n        # handle suites\n        for case in suite:\n            # handle cases\n    xml.write() # Writes back to file\n\nIt is also possible to use a custom parser. For example lxml provides a plethora\nof parsing options_. We can use them this way:\n\n.. code-block:: python\n\n    from lxml.etree import XMLParser, parse\n    from junitparser import JUnitXml\n\n    def parse_func(file_path):\n        xml_parser = XMLParser(huge_tree=True)\n        return parse(file_path, xml_parser)\n\n    xml = JUnitXml.fromfile('/path/to/junit.xml', parse_func)\n    # process xml...\n\n.. _options: https://lxml.de/api/lxml.etree.XMLParser-class.html\n\nMerge XML files\n~~~~~~~~~~~~~~~\n\nYou have two or more XML files, and you want to merge them into one.\n\n.. code-block:: python\n\n    from junitparser import JUnitXml\n\n    xml1 = JUnitXml.fromfile('/path/to/junit1.xml')\n    xml2 = JUnitXml.fromfile('/path/to/junit2.xml')\n\n    newxml = xml1 + xml2\n    # Alternatively, merge in place\n    xml1 += xml2\n\nNote that it won't check for duplicate entries. You need to deal with them on\nyour own.\n\nSchema Support\n~~~~~~~~~~~~~~~\n\nBy default junitparser supports the schema of windyroad_, which is a relatively\nsimple schema.\n\n.. _windyroad: https://github.com/windyroad/JUnit-Schema/blob/master/JUnit.xsd\n\nJunitparser also support extra schemas:\n\n.. code-block:: python\n\n    from junitparser.xunit2 import JUnitParser, TestCase, TestSuite, \\\n        RerunFailure\n    # These classes are redefined to support extra properties and attributes\n    # of the xunit2 schema.\n    suite = TestSuite(\"mySuite\")\n    suite.system_err = \"System err\" # xunit2 specific property\n    case = TestCase(\"myCase\")\n    rerun_failure = RerunFailure(\"Not found\", \"404\") # case property\n    rerun_failure.stack_trace = \"Stack\"\n    rerun_failure.system_err = \"E404\"\n    rerun_failure.system_out = \"NOT FOUND\"\n    case.add_rerun_result(rerun_failure)\n\nCurrently supported schemas including:\n\n- xunit2_, supported by pytest, Erlang/OTP, Maven Surefire, CppTest, etc.\n\n.. _xunit2: https://github.com/jenkinsci/xunit-plugin/blob/xunit-2.3.2/src/main/resources/org/jenkinsci/plugins/xunit/types/model/xsd/junit-10.xsd\n\nPRs are welcome to support more schemas.\n\nCreate XML with custom attributes\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nYou want to use an attribute that is not supported by default.\n\n.. code-block:: python\n\n    from junitparser import TestCase, Attr, IntAttr, FloatAttr\n\n    # Add the custom attribute\n    TestCase.id = IntAttr('id')\n    TestCase.rate = FloatAttr('rate')\n    TestCase.custom = Attr('custom')\n    case = TestCase()\n    case.id = 123\n    case.rate = 0.95\n    case.custom = 'foobar'\n\n\nHandling XML with custom element\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThere may be once in 1000 years you want to it this way, but anyways.\nSuppose you want to add element CustomElement to TestCase.\n\n.. code-block:: python\n\n    from junitparser import Element, Attr, TestSuite\n\n    # Create the new element by subclassing Element,\n    # and add custom attributes to it.\n    class CustomElement(Element):\n        _tag = 'custom'\n        foo = Attr()\n        bar = Attr()\n\n    testcase = TestCase()\n    custom = CustomElement()\n    testcase.append(custom)\n    # To find a single sub-element:\n    testcase.child(CustomElement)\n    # To iterate over custom elements:\n    for custom in testcase.iterchildren(CustomElement):\n        ... # Do things with custom element\n\nHandling custom XML attributes\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nSay you have some data stored in the XML as custom attributes and you want to\nread them out:\n\n.. code-block:: python\n\n    from junitparser import TestCase, Attr, JUnitXml\n\n    # Create the new element by subclassing Element or one of its child class,\n    # and add custom attributes to it.\n    class MyTestCase(TestCase):\n        foo = Attr()\n\n    xml = JUnitXml.fromfile('/path/to/junit.xml')\n    for suite in xml:\n        # handle suites\n        for case in suite:\n            my_case = MyTestCase.fromelem(case)\n            print(my_case.foo)\n\nCommand Line\n------------\n\n.. code-block:: console\n\n    $ junitparser --help\n    usage: junitparser [-h] [-v] {merge} ...\n\n    Junitparser CLI helper.\n\n    positional arguments:\n    {merge}        command\n      merge        Merge Junit XML format reports with junitparser.\n      verify       Return a non-zero exit code if one of the testcases failed or errored.\n\n    optional arguments:\n    -h, --help     show this help message and exit\n    -v, --version  show program's version number and exit\n\n\n.. code-block:: console\n\n    $ junitparser merge --help\n    usage: junitparser merge [-h] [--glob] paths [paths ...] output\n\n    positional arguments:\n      paths       Original XML path(s).\n      output      Merged XML Path, setting to \"-\" will output console\n\n    optional arguments:\n      -h, --help  show this help message and exit\n      --glob      Treat original XML path(s) as glob(s).\n      --suite-name SUITE_NAME\n                  Name added to <testsuites>.\n\n.. code-block:: console\n\n    $ junitparser verify --help\n    usage: junitparser verify [-h] [--glob] paths [paths ...]\n\n    positional arguments:\n      paths       XML path(s) of reports to verify.\n\n    optional arguments:\n      -h, --help  show this help message and exit\n      --glob      Treat original XML path(s) as glob(s).\n\nTest\n----\n\nThe tests are written with python ``unittest``, to run them, use pytest::\n\n    pytest test.py\n\nContribute\n----------\n\nPRs are welcome!\n",
    "bugtrack_url": null,
    "license": "Apache 2.0",
    "summary": "Manipulates JUnit/xUnit Result XML files",
    "version": "3.2.0",
    "project_urls": {
        "Homepage": "https://github.com/weiwei/junitparser"
    },
    "split_keywords": [
        "junit",
        "xunit",
        "xml",
        "parser"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "5af9321d566c9f2af81fdb4bb3d5900214116b47be9e26b82219da8b818d9da9",
                "md5": "a7ff006becdfcfb7734445d55b5ab053",
                "sha256": "e14fdc0a999edfc15889b637390e8ef6ca09a49532416d3bd562857d42d4b96d"
            },
            "downloads": -1,
            "filename": "junitparser-3.2.0-py2.py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "a7ff006becdfcfb7734445d55b5ab053",
            "packagetype": "bdist_wheel",
            "python_version": "py2.py3",
            "requires_python": null,
            "size": 13394,
            "upload_time": "2024-09-01T04:07:40",
            "upload_time_iso_8601": "2024-09-01T04:07:40.541797Z",
            "url": "https://files.pythonhosted.org/packages/5a/f9/321d566c9f2af81fdb4bb3d5900214116b47be9e26b82219da8b818d9da9/junitparser-3.2.0-py2.py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "57886a268028a297751ed73be8e291f12aa727caf22adbc218e8dfbafcc974af",
                "md5": "dd14d3692188087c74d5c9ebf70a861d",
                "sha256": "b05e89c27e7b74b3c563a078d6e055d95cf397444f8f689b0ca616ebda0b3c65"
            },
            "downloads": -1,
            "filename": "junitparser-3.2.0.tar.gz",
            "has_sig": false,
            "md5_digest": "dd14d3692188087c74d5c9ebf70a861d",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": null,
            "size": 20073,
            "upload_time": "2024-09-01T04:07:42",
            "upload_time_iso_8601": "2024-09-01T04:07:42.291536Z",
            "url": "https://files.pythonhosted.org/packages/57/88/6a268028a297751ed73be8e291f12aa727caf22adbc218e8dfbafcc974af/junitparser-3.2.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-09-01 04:07:42",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "weiwei",
    "github_project": "junitparser",
    "travis_ci": false,
    "coveralls": true,
    "github_actions": true,
    "requirements": [],
    "lcname": "junitparser"
}
        
Elapsed time: 9.42081s