Wednesday, May 12, 2010

How to Export to HTML in soapUI Open Source Edition

soapUI Pro has a feature to export the test result in HTML format. The open source edition can only export the test result in XML and text. I wrote a small utility to export the test result in HTML format.

I make use of Groovy templating engine here. Feel to modify it according to your needs.

To run the script, you need to include the commons-lang in the classpath and place the report.template in the same directory as the RunSoapUiTest.groovy. Enjoy :)

RunSoapUiTest.groovy
import groovy.text.GStringTemplateEngine
import org.apache.commons.lang.StringEscapeUtils

final TEMPLATE_FILE = "report.template"

class TestSuite {
    def name
    def numTestCases
    def numFailures
    def testCases
}

class TestCase {
    def name
    def status
    def type
    def time
}

def createReport(def template, def testSuite, def outputFile) {
    def templateFile = new File(template)
    def outFile = new File(outputFile)
    if (outFile.exists()) {
        outFile.renameTo(new File(outFile.path + "." + new Date(outFile.lastModified()).format("yyyyMMdd-HHmmss")))
    }
    def binding = ["testSuite":testSuite]
    def t = new GStringTemplateEngine().createTemplate(templateFile).make(binding)
    def writer = new PrintWriter(outputFile)
    try {
        t.writeTo(writer)
    } finally {
        writer?.close()
    }
}

def getTestSuite(def testResultDir) {
    def testSuite = new TestSuite()
    testSuite.numTestCases = 0
    testSuite.numFailures = 0
    testSuite.testCases = []
   
    def dir = new File(testResultDir)
    dir.eachFile { file ->
        if (file.name.endsWith(".txt")) {
            def splitStr = file.name.split('-')
            testSuite.name = splitStr[0]
            testSuite.numTestCases += 1
            testSuite.numFailures += (splitStr[splitStr.size()-1].startsWith("OK")) ? 0 : 1
           
            def testCase = new TestCase()
            testCase.name = ''
            testCase.type = '
'
           
            (1..splitStr.size()-3).each {
                testCase.name += splitStr[it] + ' '
            }
            testCase.status = (splitStr[splitStr.size()-1].startsWith("OK")) ? 'Success' : 'Failure'
            def content = false
            file.eachLine { line ->
                if (line.startsWith('Time Taken')) {
                    testCase.time = line.split(':')[1].trim()
                } else if (line.contains('- Request -')) {
                    content = true
                }   
               
                if (content) {
                    testCase.type += StringEscapeUtils.escapeHtml(line) + '\n
'
                }
            }
            testCase.type += '
' testSuite.testCases += testCase } } return testSuite } def printUsage() { println "Usage: groovy RunSoapUiTest.groovy <test_suite_name> <soapUI_project> <test_results_dir> [host:port]" } def validateArgs(def args) { if (args.size() >= 3 && args.size() <= 4) { if (!new File(args[1]).isFile()) { return false } if (!new File(args[2]).isDirectory()) { return false } } return true } def cleanUp(def dir) { new File(dir).eachFile { file -> if (file.name.endsWith(".xml") || file.name.endsWith(".txt")) { file.delete() } } } if (System.env['SOAPUI_HOME'] == null) { println "Please set the SOAPUI_HOME environment variable" System.exit(1) } println "SOAPUI_HOME=${System.env['SOAPUI_HOME']}" if (!validateArgs(this.args)) { printUsage() System.exit(1) } def testSuiteName = args[0] def soapUiProject = args[1] def testResultsDir = args[2] def host = (args.size() == 4) ? "-h" + args[3] : "" def cmd = "\"${System.env['SOAPUI_HOME'] + File.separator + "bin" + File.separator + "testrunner"}\" " + "${host} -s\"${testSuiteName}\" -a -j -f \"${testResultsDir}\" -I \"${soapUiProject}\"" println "Command=${cmd}" cmd.execute().waitForProcessOutput(System.out, System.err) def testSuite = getTestSuite(testResultsDir) createReport(TEMPLATE_FILE, testSuite, testResultsDir + File.separator + testSuiteName + "_TestReport-" + Calendar.getInstance().format("yyyyMMdd") + ".html") cleanUp(testResultsDir) println "***** Done *****"

report.template
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html xmlns:stringutils="xalan://com.eviware.soapui.support.StringUtils" xmlns:lxslt="http://xml.apache.org/xslt">
<head>
<META http-equiv="Content-Type" content="text/html; charset=US-ASCII">
<title>soapUI Test Results</title>
<style type="text/css">
body {
font:normal 68% verdana,arial,helvetica;
color:#000000;
}
table tr td, table tr th {
font-size: 68%;
}
table.details tr th{
font-weight: bold;
text-align:left;
background:#a6caf0;
}
table.details tr td{
background:#eeeee0;
}

p {
line-height:1.5em;
margin-top:0.5em; margin-bottom:1.0em;
}
h1 {
margin: 0px 0px 5px; font: 165% verdana,arial,helvetica
}
h2 {
margin-top: 1em; margin-bottom: 0.5em; font: bold 125% verdana,arial,helvetica
}
h3 {
margin-bottom: 0.5em; font: bold 115% verdana,arial,helvetica
}
h4 {
margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
}
h5 {
margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
}
h6 {
margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
}
.Error {
font-weight:bold; color:red;
}
.Failure {
font-weight:bold; color:purple;
}
.Properties {
text-align:right;
}
</style>
</head>
<body>
<a name="top"></a>
<h1>soapUI Test Results</h1>
<hr size="1">
<h2>Summary of ${testSuite.name}</h2>
<table width="95%" cellspacing="2" cellpadding="5" border="0" class="details">
<tr valign="top">
<th>TestCases</th><th>Failures</th><th>Success Rate (%)</th>
</tr>
<tr valign="top" class="Failure">
<td>${testSuite.numTestCases}</td><td>${testSuite.numFailures}</td><td>${((testSuite.numTestCases - testSuite.numFailures) / testSuite.numTestCases) * 100}</td>

</tr>
</table>
<table width="95%" border="0">
<tr>
<td style="text-align: justify;">
Note: <i>failures</i> are anticipated and checked for with assertions while <i>errors</i> are unanticipated.
</td>
</tr>
</table>
<p></p>
<hr align="left" width="95%" size="1">
<h3>${testSuite.name}</h3>
<table width="95%" cellspacing="2" cellpadding="5" border="0" class="details">
<tr valign="top">
<th>TestCase</th><th>Status</th><th width="80%">Type</th><th nowrap>Time (s)</th>
</tr>
<% testSuite.testCases.each { testCase -> %>
<tr valign="top" class="${testCase.status == 'Failure' ? 'Error' : ''}">
<td>${testCase.name}</td><td>${testCase.status}</td><td>${testCase.type}</td><td>${testCase.time}</td>
</tr>
<% } %>
</table>
<p></p>
<a href="#top">Back to top</a>
</body>
</html>

3 comments:

  1. This looks to solve the purpose but i'm unable to implement it.. how to use this code..from within the SoapUI tool or call it from out side. where should this be placed. Say, I have a project that had 2 suites with a set of Req_responses. how should this be implemented??

    ReplyDelete
  2. Could you please tell us how should this be implemented?

    ReplyDelete
  3. This comment has been removed by the author.

    ReplyDelete