"""
This script can be used to write test results to XML file in JUnit
format. Not all tags supported by JUnit are provided, but the script
can easily be expanded to support them.
See function main() below for example of usage.
"""
class TestResult:
"""
This class contains result of one test case.
"""
def __init__(self, testId, testedItem,
isError=False,
isFailure=False,
errorFailureType='',
errorFailureDesc=''):
self._testId = testId
self._testedItem = testedItem
self._isError = isError
self._isFailure = isFailure
self._errorFailureType = errorFailureType
self._errorFailureDesc = errorFailureDesc
def getTestId(self):
return self._testId
def getTestedItem(self):
return self._testedItem
def isError(self):
return self._isError
def isFailure(self):
return self._isFailure
def getErrorFailureType(self):
return self._errorFailureType
def getErrorFailureDesc(self):
return self._errorFailureDesc
class _TestReportStatistics:
"""
This class contains test statistics. For internal usage only.
"""
def __init__(self, noOfErrors, noOfFailures):
self._noOfErrors = noOfErrors
self._noOfFailures = noOfFailures
def getNoOfErrors(self):
return self._noOfErrors
def getNoOfFailures(self):
return self._noOfFailures
def saveTestResultsAsJUnit(fileName, testSuiteName, testResults):
"""
This method saves test results in Junit format, so that it can be parsed
by Jenkins/Hudson. This method creates file with one test suite.
For details about the format see:
https://stackoverflow.com/questions/4922867/junit-xml-format-specification-that-hudson-supports
XSD:
https://svn.jenkins-ci.org/trunk/hudson/dtkit/dtkit-format/dtkit-junit-model/src/main/resources/com/thalesgroup/dtkit/junit/model/xsd/junit-4.xsd
Parameters:
fileName - name of file to save report to
testSuiteName - name of test suite which is used for attribute name::
'<testsuite name="' + testSuiteName + '" ...
testResults - list of TestResult instances
Example of generated file::
<testsuites name="testIDEATestSuite" tests="3" errors="0" failures="0">
<testsuite name="testIDEATestSuite" tests="3" errors="0" failures="0">
<testcase classname="add_int" name="test-0"/>
<testcase classname="add_int" name="test-1"
<error type="exception"/>Invalid value!</error>
</testcase>
<testcase classname="max_int" name="test-2">
<failure type="expression"></failure>
</testcase>
</testsuite>
</testsuites>
"""
reportStats = _getReportStatistics(testResults)
with open(fileName, 'w') as of:
_saveTestResultsAsJUnitStart(of,
testSuiteName,
len(testResults),
reportStats.getNoOfErrors(),
reportStats.getNoOfFailures())
_saveTestResultsAsJUnitForTestSuite(of,
testSuiteName,
testResults,
reportStats)
_saveTestResultsAsJUnitEnd(of)
def _saveTestResultsAsJUnitStart(of, testSuitesName, noOfTests, noOfErrors,
noOfFailures):
"""
This method saves test results in Junit format, so that it can be parsed
by Jenkins/Hudson. Typical usage:
saveTestResultsAsJUnitStart(...)
saveTestResultsAsJUnitForTestSuite(...)
saveTestResultsAsJUnitEnd()
Parameters:
of - opened file stream
testSuitesName - values for the 'name' attribute of tags 'testsuites' and
'testsuite'
noOfTests - number of all tests in test suite
noOfErrors - number of errors
noOfFailures - number of failures
For details about format see method _saveTestResultsAsJUnit().
"""
of.write('<?xml version="1.0" encoding="UTF-8"?>\n')
of.write('<testsuites name="' + testSuitesName + '" tests="' +
str(noOfTests) + '" errors="' +
str(noOfErrors) + '" failures="' +
str(noOfFailures) + '">\n')
def _saveTestResultsAsJUnitForTestSuite(of, testSuiteName, testResults, reportStats):
"""
This method saves test results in JUnit format, so that it can be parsed
by Jenkins/Hudson for one test suite. Typical usage:
saveTestResultsAsJUnitStart(...)
saveTestResultsAsJUnitForTestSuite(...)
saveTestResultsAsJUnitEnd()
See method _saveTestResultsAsJUnitStart() for details.
"""
of.write(' <testsuite name="' + testSuiteName + '" tests="' +
str(len(testResults)) + '" errors="' +
str(reportStats.getNoOfErrors()) + '" failures="' +
str(reportStats.getNoOfFailures()) + '">\n')
for tr in testResults:
of.write(' <testcase classname="' + tr.getTestedItem() + '" name="' +
tr.getTestId() + '">\n')
if tr.isError():
of.write(' <error type="' + tr.getErrorFailureType() + '">')
of.write(_replaceXMLEntities(tr.getErrorFailureDesc()))
of.write('</error>\n')
if tr.isFailure():
of.write(' <failure type="' + tr.getErrorFailureType() + '">')
of.write(_replaceXMLEntities(tr.getErrorFailureDesc()))
of.write('</failure>\n')
of.write(' </testcase>\n')
of.write(' </testsuite>\n')
def _saveTestResultsAsJUnitEnd(of):
"""
This method adds the last tag to XML file in Jenkins/Hudson format.
See method saveTestResultsAsJUnitStart() for details.
"""
of.write('</testsuites>\n')
def _getReportStatistics(testResults):
noOfErrors = 0
noOfFailures = 0
for tr in testResults:
if tr.isError():
noOfErrors += 1
if tr.isFailure():
noOfFailures += 1
return _TestReportStatistics(noOfErrors, noOfFailures)
def _replaceXMLEntities(message):
message = message.replace('<', '<')
message = message.replace('>', '>')
message = message.replace('&', '&')
message = message.replace('"', '"')
message = message.replace("'", ''')
return message
def main():
"""
This function demonstrates usage of this module.
"""
testResults = [TestResult('test-01', 'HandBrake'),
TestResult('test-02', 'HandBrake'),
TestResult('test-03', 'HandBrake', isFailure = True,
errorFailureType='expression',
errorFailureDesc='voltage < 3.14'),
TestResult('test-03', 'HandBrake', isError = True,
errorFailureType='exception',
errorFailureDesc='FileNotFoundException')
]
outFName = 'testReport.xml'
saveTestResultsAsJUnit(outFName, 'iSYSTEM HIL test', testResults)
print("Sample report saved to '" + outFName + "'")
if __name__ == '__main__':
main()