Tuesday 25 March 2008

XML automation - Part V - Validate XML response

This is the last part...but I will try to summarize the posts in an article later on...maybe I will add more features, for example how to handle attributes and multiple elements.

All the example code and files are available in compressed format here for download.

Please note that I have changed the test case parameter ConvertTempResult to #ConvertTempResult in order to mark up output parameters.



Here is the function for response validation:



Function validateXMLResponse (objDictionary)

Set xmlDoc = CreateObject("Msxml2.DOMDocument")
'loadXML
xmlDoc.loadXML(objDictionary.Item("XMLResponse"))
'Check XML syntax
If (xmlDoc.parseError.errorCode <> 0) Then
Set myErr = xmlDoc.parseError
objDictionary.Item("XMLResponseResult") = "Failed"
objDictionary.Item("XMLResponseResultDetails") = "ERROR: " & myErr.reason
Set validateXMLResponse = objDictionary
Exit Function
End If
Set root = xmlDoc.documentElement
'Put all test parameters in array
arrAllKeys = objDictionary.Keys
arrAllValues = objDictionary.Items
'Update values
For i = 0 to Ubound(arrAllKeys)
'Only process #-type parameters
If Left(arrAllKeys(i),1) = "#" Then
strParameterElementName = arrAllKeys(i)
strParameterElementName = Right(strParameterElementName,Len(strParameterElementName)-1)
strParameterElementValueExpected = arrAllValues(i)
'Get element and value
Set node = root.getElementsByTagName(strParameterElementName)
If node.Length > 0 Then
For n = 0 to node.Length-1
'Return actual value in [element]_Out
objDictionary.Item(strParameterElementName & "_Out") = node.Item(n).Text
strParameterElementValueActual = node.Item(n).Text
'Set result
If strParameterElementValueExpected = strParameterElementValueActual AND _
objDictionary.Item("XMLResponseResult") <> "Failed" Then
objDictionary.Item("XMLResponseResult") = "Passed"
Else
objDictionary.Item("XMLResponseResult") = "Failed"
objDictionary.Item("XMLResponseResultDetails") = objDictionary.Item("XMLResponseResultDetails") & _
strParameterElementName & " = " & strParameterElementValueActual & " , expected value " & _
strParameterElementValueExpected & " ;" 'use ; as separator
End If
Next
End If
Set node = Nothing
End If
Next
'Return
Set validateXMLResponse = objDictionary
'Clean up
Set root = Nothing
Set xmlDoc = Nothing
End Function


So finally we have these steps all together:



' Example of XML/Web Service automation in VBScript using WinHttp, XML DOM and ADO
' by Stefan Thelenius march 2008

'Set test case id and data source file
intTestCaseID = 1
strMyDataFile = "c:\MyTestData.xls"

'Get test case parameters
Set objMyTestParameters = getTestParametersIntoDictionary (strMyDataFile, _
"TestCase", "TestCaseID", intTestCaseID)

'Get global parameters
Set objMyGlobalParameters = getTestParametersIntoDictionary (strMyDataFile, _
"Global", "GlobalID", objMyTestParameters.Item("GlobalID"))

'Merge parameters
Set objMyTestParameters = mergeDictionaries (objMyGlobalParameters, objMyTestParameters)

'Create request from template
objMyTestParameters.Item("XMLRequest") = getXMLTemplate (objMyTestParameters.Item("XMLTemplateFile"), _
objMyTestParameters.Item("XMLTemplate"))

'Insert test parameters
objMyTestParameters.Item("XMLRequest") = setTestParametersInXMLRequest (objMyTestParameters)

'Send request
objMyTestParameters.Item("XMLResponse") = sendXMLRequest (objMyTestParameters)

'Validate response
Set objMyTestParameters = validateXMLResponse (objMyTestParameters)

'Display output
Msgbox objMyTestParameters.Item("XMLResponse")
Msgbox objMyTestParameters.Item("XMLResponseResult")
Msgbox objMyTestParameters.Item("XMLResponseResultDetails")
Msgbox objMyTestParameters.Item("ConvertTempResult_Out")

Wednesday 19 March 2008

AdvancedQTP

I am joining forces with the team behind AdvancedQTP as a moderator for some of their forums. The site is an excellent resource regarding QTP and vbScript and I strongly recommend it as first stop knowledge base since HP/Mercury's dito is hard to find these days...

Tuesday 11 March 2008

XML automation - Part IV - Sending XML request

This week it is time to send the Web Service request and recieve a response...I have also added some Global parameters in a new sheet in my Excel file so that we can handle test environment properties and so on:



A also added a function for merging Dictionary objects which I use sometimes in my framework. It might not be the best code design approach but I think the concept works well, create a number of dictionaries, merge them together and pass one dictionary to the test case including all test parameters.
Function mergeDictionaries (objDIC1, objDIC2)
If IsObject(objDIC2) Then
allKeys = objDIC2.Keys
allItems = objDIC2.Items
For i = 0 To objDIC2.Count - 1 'Iterate through the array
If objDIC1.Exists(allKeys(i)) Then
'Value in objDIC2 is master
objDIC1.Item(allKeys(i)) = allItems(i)
Else
objDIC1.Add allKeys(i),allItems(i)
End If
Next
End If
Set mergeDictionaries = objDIC1
End Function
And finally the funcion for sending the request



Function sendXMLRequest (objDictionary)
Dim WinHttpReq
Const HTTPREQUEST_SETCREDENTIALS_FOR_SERVER = 0
Set WinHttpReq = CreateObject("WinHttp.WinHttpRequest.5.1")
WinHttpReq.Open "POST", objDictionary.Item("PostURL"), False
WinHttpReq.setRequestHeader "Content-Type", "text/xml;charset=utf-8"
WinHttpReq.setRequestHeader "Accept", _
"text/xml, multipart/related, text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2"
WinHttpReq.setRequestHeader "Content-Transfer-Encoding", "binary"
WinHttpReq.setRequestHeader "Connection", "keep-alive"
WinHttpReq.setRequestHeader "SOAPAction", objDictionary.Item("SOAPAction")
If objDictionary.Item("UserName") <> "" Then
WinHttpReq.SetCredentials objDictionary.Item("UserName"), objDictionary.Item("Password"), _
HTTPREQUEST_SETCREDENTIALS_FOR_SERVER
End If
'Add WS header and foot if SOAP
If objDictionary.Item("RequestType") = "SOAP" Then
strHeader = "<?xml version=""1.0"" encoding=""utf-8""?>" &_
"<soap:Envelope xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance""" &_
" xmlns:xsd=""http://www.w3.org/2001/XMLSchema""" &_
" xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/"">" &_
"<soap:Body>"
strFoot = "</soap:Body></soap:Envelope>"
End If
WinHttpReq.Send(strHeader & objDictionary.Item("XMLRequest") & strFoot)
sendXMLRequest = WinHttpReq.ResponseText
End Function


So all together it should look like this:



intTestCaseID = 1
Set objMyTestParameters = getTestParametersIntoDictionary ("c:\MyTestData.xls", _
"TestCase", "TestCaseID", intTestCaseID)
Set objMyGlobalParameters = getTestParametersIntoDictionary ("c:\MyTestData.xls", _
"Global", "GlobalID", objMyTestParameters.Item("GlobalID"))
Set objMyTestParameters = mergeDictionaries (objMyGlobalParameters, objMyTestParameters)
objMyTestParameters.Item("XMLRequest") = getXMLTemplate (objMyTestParameters.Item("XMLTemplateFile"), _
objMyTestParameters.Item("XMLTemplate"))
objMyTestParameters.Item("XMLRequest") = setTestParametersInXMLRequest (objMyTestParameters)
objMyTestParameters.Item("XMLResponse") = sendXMLRequest (objMyTestParameters)
Msgbox objMyTestParameters.Item("XMLResponse")


Up next: Part V - Validate the response

Monday 3 March 2008

XML automation - part III (test parameters)

Now it is time to set some test parameters in our XML template (see previous post). In my test framework I handle test parameters in an SQL Server database but in my examples here I use good old Excel instead.

First, create and save an Excel file with your test parameters:


Second, new functions for reading parameters into Dictionary Object and set parameters in an xml:

strXMLTemplateFile = "c:\Templates.xml"
intTestCaseID = 1
Set objMyTestParameters = getTestParametersIntoDictionary ("c:\MyTestData.xls", "TestCase", "TestCaseID", intTestCaseID)
objMyTestParameters.Item("XMLRequest") = getXMLTemplate (strXMLTemplateFile, objMyTestParameters.Item("XMLTemplate"))
objMyTestParameters.Item("XMLRequest") = setTestParametersInXMLRequest (objMyTestParameters)
Msgbox objMyTestParameters.Item("XMLRequest")

Function getTestParametersIntoDictionary (strFileName, strSheetName, strKeyColumn, intKeyID)
'Initiate Dictionary
Set getTestParametersIntoDictionary = CreateObject("Scripting.Dictionary")
'Initiate Database connection
Set objDB = CreateObject("ADODB.Connection")
'Open connection
objDB.Open "Provider=Microsoft.Jet.OLEDB.4.0;Data Source="&strFileName&_
";Extended Properties=""Excel 8.0;HDR=Yes;IMEX=1"";"
strQuery="select * from [" & strSheetName & "$] where " & strKeyColumn & "=" & intKeyID
'Create a record set, execute query and store in dictionary
Set objRec = objDB.Execute(strQuery)
Do Until objRec.EOF
For Each x In objRec.Fields
'Convert NULL to empty string or clean from spaces
strCurrentValue = x.Value
If IsNull(strCurrentValue) Then
strCurrentValue = ""
Else
strCurrentValue = Trim(strCurrentValue)
End If
'Add to dictionay
getTestParametersIntoDictionary.Add Trim(x.Name), strCurrentValue
Next
objRec.MoveNext
Loop
'Clean up
objRec.Close
Set objRec = Nothing
objDB.Close
Set objDB = Nothing
End Function

Function setTestParametersInXMLRequest (objDictionary)
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
'loadXML
xmlDoc.loadXML(objDictionary.Item("XMLRequest"))
'Check XML syntax
If (xmlDoc.parseError.errorCode <> 0) Then
Set myErr = xmlDoc.parseError
Msgbox "ERROR: " & myErr.reason
End If
Set root = xmlDoc.documentElement
'Put all test parameters in array
arrAllKeys = objDictionary.Keys
arrAllValues = objDictionary.Items
'Update values
For i = 0 to Ubound(arrAllKeys)
'Only process $-type parameters and where values <> ""
If Left(arrAllKeys(i),1) = "$" AND arrAllValues(i) <> "" Then
strParameterElementName = arrAllKeys(i)
strParameterElementName = Right(strParameterElementName,Len(strParameterElementName)-1)
strParameterElementValue = arrAllValues(i)
'Insert element and value
Set node = root.getElementsByTagName(strParameterElementName)
If node.Length > 0 Then
For n = 0 to node.Length-1
'Replace all
node.Item(n).Text = strParameterElementValue
Next
End If
Set node = Nothing
End If
Next
'Return xml request
setTestParametersInXMLRequest = root.xml
'Clean up
Set root = Nothing
Set xmlDoc = Nothing
End Function
Up next: Part IV - Sending XML request

XML automation Part I and II re-make

I found some public Web Services the other day (http://www.webservicex.net/) and thought using them in my examples would make it a bit more realistic...

So I have reworked my previous examples:

Test client

<html>
<!--
*********************************************************************************************************************
Purpose: To send XML http request.
Assumptions: Proper security settings in browser and DLL for WinHttp5.1 object
Author: Stefan Thelenius 2008-03-03.
'********************************************************************************************************************
-->
<head>
<title>Web Service Test Tool</title>
<script language="JavaScript">
self.resizeTo(screen.width,screen.height)
</script>
</head>
<body>
<style>
select {font-family: verdana; font-size: 8pt; font-weight: 500;}
input {font-family: verdana; font-size: 8pt; font-weight: 500;}
textarea {font-family: verdana; font-size: 8pt; font-weight: 500;}
</style>
<FONT FACE="verdana" SIZE=3>
<center>Web Service Test Tool</center>
<FONT FACE="verdana" SIZE=1>
<FORM NAME="form">
<SCRIPT LANGUAGE="vbscript">
document.write("<table width=""100%"" border=""0"" cellspacing=""0"" cellpadding=""0"">")
document.write("<tr>")
document.write("<td><left>")
document.write("<FONT FACE=""verdana"" SIZE=1>URL&nbsp")
document.write("<INPUT TYPE=""text"" NAME=""url"" SIZE=70 TITLE=""URL"">")
document.write("<FONT FACE=""verdana"" SIZE=1>&nbspSOAPAction&nbsp")
document.write("<INPUT TYPE=""text"" NAME=""soap"" SIZE=70 TITLE=""SOAP Action"">")
document.write("</td></tr>")
document.write("<tr>")
document.write("<td><left>")
document.write("<FONT FACE=""verdana"" SIZE=1>Username&nbsp")
document.write("<INPUT TYPE=""text"" NAME=""username"" SIZE=20 TITLE=""UserName"">")
document.write("<FONT FACE=""verdana"" SIZE=1>&nbsp&nbspPassword&nbsp")
document.write("<INPUT TYPE=""password"" NAME=""password"" SIZE=12>")
document.write("</left>")
document.write("<FONT FACE=""verdana"" SIZE=1>&nbsp&nbspResponse to Clipboard")
document.write("<input type=""checkbox"" name=""blnSave"">")
document.write("</td></tr>")
document.write("<br>")
document.write("<table width=""100%"" border=""0"" cellspacing=""0"" cellpadding=""0"">")
document.write("<tr>")
'Autofit request and response textarea
intWidth = screen.width / 13
intHeight = screen.height / 23
document.write("<td><FONT FACE=""verdana"" SIZE=1>Request<BR>")
document.write("<textarea name=""IN"" rows=""" & intHeight & """ cols=""" & intWidth & """>")
document.write("</textarea></td>")
document.write("<td><FONT FACE=""verdana"" SIZE=1>Response<BR>")
document.write("<textarea name=""OUT"" rows=""" & intHeight & """ cols=""" & intWidth & """>")
document.write("</textarea></td>")
document.write("</tr><tr><td>")
document.write("<INPUT TYPE=""button"" NAME=""cmdSend"" VALUE=""Send"" onclick=""SendRequest()"">")
document.write("&nbsp<INPUT TYPE=""button"" NAME=""cmdPretty"" VALUE=""Parse input"" onclick=""ParseInput()"">")
document.write("&nbsp<INPUT TYPE=""button"" NAME=""cmdClean"" VALUE=""Clean output"" onclick=""CleanResponse()"">")
document.write("</td><td id=""tid""><FONT FACE=""verdana"" SIZE=1>&nbsp</td>")
document.write("</tr></table></FORM>")
'Get current user as default
Dim objNet
Set objNet = CreateObject("WScript.NetWork")
form.username.Value = objNet.UserDomain & "\" & objNet.UserName
Set objNet = Nothing
Sub SendRequest
Dim WinHttpReq
Const HTTPREQUEST_SETCREDENTIALS_FOR_SERVER = 0
Set WinHttpReq = CreateObject("WinHttp.WinHttpRequest.5.1")
strStart = Timer
WinHttpReq.Open "POST", form.url.Value, False
WinHttpReq.setRequestHeader "Content-Type", "text/xml;charset=utf-8"
WinHttpReq.setRequestHeader "Accept", _
"text/xml, multipart/related, text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2"
WinHttpReq.setRequestHeader "Content-Transfer-Encoding", "binary"
WinHttpReq.setRequestHeader "Connection", "keep-alive"
WinHttpReq.setRequestHeader "SOAPAction", form.soap.Value
WinHttpReq.SetCredentials form.username.Value, form.password.Value, _
HTTPREQUEST_SETCREDENTIALS_FOR_SERVER
WinHttpReq.Send(form.IN.Value)
strStop = Timer
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
'Parse output
xmlDoc.loadXML(WinHttpReq.ResponseText)
If (xmlDoc.parseError.errorCode <> 0) Then
form.OUT.Value = WinHttpReq.ResponseText
Else
form.OUT.Value = xmlDoc.xml
End If
Set xmlDoc = Nothing
strTime = strStop - strStart
strTime = FormatNumber(strTime,2)
tid.Innertext = "Response time: " & strTime & " s"
Set WinHttpReq = Nothing
'Set clipboard = output
If form.blnSave.Value = True Then window.clipboardData.setData "Text",form.OUT.Value
End Sub
Sub ParseInput
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.loadXML(form.IN.Value)
If (xmlDoc.parseError.errorCode <> 0) Then
Set myErr = xmlDoc.parseError
Msgbox "ERROR: " & myErr.reason
Set myErr = Nothing
Set xmlDoc = Nothing
Exit Sub
End If
form.IN.Value = xmlDoc.xml
Set xmlDoc = Nothing
End Sub
Sub CleanResponse
form.OUT.Value = ""
End Sub
</SCRIPT>
</body>
</html>
XML Templates (save into file c:\Templates.xml)

<?xml version="1.0" ?>
<Templates>
<myTemplate_01>
<ConvertTemp xmlns="http://www.webserviceX.NET/">
<Temperature>10</Temperature>
<FromUnit>degreeCelsius</FromUnit>
<ToUnit>degreeFahrenheit</ToUnit>
</ConvertTemp>
</myTemplate_01>
<myTemplate_02>
<ConvertTemp xmlns="http://www.webserviceX.NET/">
<Temperature>60</Temperature>
<FromUnit>degreeFahrenheit</FromUnit>
<ToUnit>degreeCelsius</ToUnit>
</ConvertTemp>
</myTemplate_02>
</Templates>
XML template function

strXMLTemplateFile = "c:\Templates.xml"
strMyTemplate = "myTemplate_01"
Msgbox getXMLTemplate (strXMLTemplateFile, strMyTemplate)
Function getXMLTemplate (strXMLTemplateFile, strMyTemplate)
Set xmlDoc = CreateObject("Msxml2.DOMDocument")
'loadXML
xmlDoc.load(strXMLTemplateFile)
'Check XML syntax
If (xmlDoc.parseError.errorCode <> 0) Then
Set myErr = xmlDoc.parseError
Msgbox "ERROR: " & myErr.reason
End If
Set root = xmlDoc.documentElement
'Select template and return
Set node = root.selectNodes("//" & strMyTemplate)
If node.Length > 0 Then
getXMLTemplate = node.Item(0).childNodes.Item(0).xml
Else
getXMLTemplate = "No template found!"
End If
'Clean up
Set xmlDoc = Nothing
Set root = Nothing
Set node = Nothing
End Function