Monday, August 28, 2006

Groovy: Parsing an XML file with Groovy

Groovy comes with a handy XML parser. With this parser you can load an XML file quickly and navigate it easy.

Currently I'm working with WebTest (http://webtest.canoo.com/) for testing functional aspects of an web application.

WebTest comes with a nice overview to present the results of the test runs. The report itself is transformed out of an XML file. For statistics reason, I'm using this XML file to extract specific data. This extraction is done with a little Groovy script.

To get an idea of the WebTest report XML file, have a look a the fragment below:

WebTestReport.xml:








...










name="resultFilename" value="response_1156746643278_invoke.html"/>
















...



This file can be parsed with the following code:


import java.text.SimpleDateFormat

FormatUS = new SimpleDateFormat('EEE MMM dd hh:mm:ss z yyyy', Locale.US)
FormatDE = new SimpleDateFormat('dd.MM.yyyy hh:mm:ss', Locale.GERMAN)

def folder = args?.size() ? args[0] : "data"
println "reading files from directory '$folder'"
def basedir = new File(folder)

// result files of the current run
files = basedir.listFiles().grep(~/.*xml$/)

List resultLines() {
List result = []
for (currentFile in files) {
println " processing $currentFile"
def summary = new XmlParser().parse(currentFile)

// List with all 'testresult' elements
def testresults = summary.testresult


testresults.each { testcase ->
def entry = [:]
entry.name = testcase.'@testspecname'.split(/\W/)[0]
entry.time = testcase.'@starttime'
entry.time = FormatUS.parse(entry.time)
entry.time = FormatDE.format(entry.time)
entry.ok = testcase.'@successful'

def rt = testcase.depthFirst().findAll { element ->
if (element.name() != 'parameter') return false
if (element.'@name' != 'taskName') return false
def roundtripTags = 'clickLink clickButton clickElement'
roundtripTags.tokenize().contains(element.'@value')
}
entry.roundtrips = rt.size()

def fields = testcase.depthFirst().findAll { element ->
if (element.name() != 'parameter') return false
if (element.'@name' != 'taskName') return false
def fieldsTags = 'selectForm setCheckbox setFileField setInputField setRadioButton'
fieldsTags.tokenize().contains(element.'@value')
}
entry.fields = fields.size()

def verifies = testcase.depthFirst().findAll { element ->
if (element.name() != 'parameter') return false
if (element.'@name' != 'taskName') return false
def fieldsTags = 'verifyCheckbox verifyCookie verifyDocumentURL verifyElement'
fieldsTags.tokenize().contains(element.'@value')
}
entry.verifies = verifies.size()

if (testcase.results.error) {
entry.resultsError = testcase.results.error.'@exception'[0]
} else {
entry.resultsError = ''
}

/*
if (testcase.results.failure) {
entry.resultsFailure = testcase.results.failure.'@message'[0].replaceAll(/\W/, " ")
println "####" + entry.resultsFailure
} else {
entry.resultsFailure = ''
}
*/

def groupSteps = testcase.results.step.findAll { step ->
step.parameter.any { param ->
param.'@name' == 'taskName' && param.'@value' == 'group'
}
}

def goodSteps = groupSteps.findAll{ it.result?.completed }

// assumed: there is always at least one group step, size() never 0
if (groupSteps.size() != 0) {
entry.complete_pct = goodSteps.size() / groupSteps.size()
} else {
entry.complete_pct = 0
}

if (goodSteps) {
entry.description = goodSteps[-1].parameter.findAll{it.'@name'=='description'}.'@value'[0]
} else {
entry.description = ''
}

result <<>
writer <<
"Testfall\t" <<
"Datum\t" <<
"Erfolgreich\t" <<
"Prozent\t" <<
"Error\t" <<
"Server Roundtrips\t" <<
"Number of Fields\t" <<
"Verifications\t" <<
"Beschreibung\n"
for (entry in resultLines()) {
writer <<
entry.name << "\t" <<
entry.time << "\t" <<
entry.ok << "\t" <<
entry.complete_pct << "\t" <<
entry.resultsError << "\t" <<
entry.roundtrips << "\t" <<
entry.fields << "\t" <<
entry.verifies << "\t" <<
entry.description << "\n"
}
}


Now you get a tab seperated file with the content of your result files from the web testing runs. I
import this file into Excel and create a pivot table to understand the results better.

I hope this helps :)

No comments: