Monday, 3 March 2008

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

25 comments:

Syed said...

Hi Stefan,
First of all thanks for making your code to use by others....
Actually we have a new requirement of doing web service testing and without any knowledge i was just struggling how to step in. Really your code helps me to make understand the entire process.
I have taken web service testing code and try to run it.
I got an error on the line
WinHttpReq.Send(strHeader & objDictionary.Item("XMLRequest") & strFoot) saying "A connection with the server could not be established". I could understand that it was not unable to reach the server but still am not a technical person to rectify it and resume...please help me out on this....
if possible please send your respose to syed.johny@gmail.com...

Stefan said...

Hi,

I prefer to help via Comments here since it might help other users with simular problems.

Have you tried my example ?

Syed said...

Yeah i tried...actually i ran your code and got this error on the line i mentioned in my previous comment. Till datas from the excel getting inserted into the dictionary were ran.
When i try to debug the object "WinHttpReq" - no values had been set for their attributes. thanks for your prompt reply

Stefan said...

I just tried my example and it worked "out-of-the-box" if you place all files directly under c:\

Syed said...

Yeah i followed whatever you said but still am getting the same error. I am able to access the web service directly from the browser and not through code.

Syed said...

Might the tool kit be an issue in this case..I am using QTP 9.2, web service add-ins 9.2 and toolkit ".NET Framework 2.0 WSE 3.0". Please send me your version details. I really thought i could complete my task after getting completely into your code, but this error has stopped everything.

Stefan said...

Try this code snippet and see if it works:

Set WinHttpReq = CreateObject("WinHttp.WinHttpRequest.5.1")
WinHttpReq.Open "GET", "http://www.google.com/", False
WinHttpReq.setRequestHeader "Connection", "keep-alive"
WinHttpReq.Send
Msgbox WinHttpReq.ResponseText

You should get the HTML source in your response.

My previous example should work in any QTP version since it is standard VBScript using standard windows objects. It also works as vbs standalone.

Syed said...

This time "The operation timed out"....Lemme try this code in my home with the standalone machine; send the request to my local host and check......
But using "Web Service Testing wizard" i just gave my WSDL URL and it automatically frames a code through which i could send the request and got the response...
Really thanks for your efforts

Syed said...

Hi Stefan,
I need to load an excel file values(both header and datas) to an object. Is it possible??? If so how to get it and then how to retrieve those values from that object.
I have done a sample script...if you have any new scripts apt to my requirement please post it or else please update my existing code and post it.

GetInputFile
Public Function GetInputFile
Dim strFileName
Set ExcelObj = CreateObject("Excel.Application")
Set objWorkbook = ExcelObj.Workbooks.open("C:\Documents and Settings\jsyedabu\Desktop\InputFile.xls")
intColumn = 1
Do Until ExcelObj.Cells(1,intColumn).Value = ""
Msgbox ExcelObj.Cells(1,intColumn).Value
intColumn = intColumn + 1
Loop
ExcelObj.Quit
End Function

Stefan said...

I have an example of using ADO with Excel here

Using ADO it is possible to get column name and values into a record set object.

Syed said...

Thanks...Actually i gone though your complete webservice testing code and its there how to fecth all the excel values and store it in the ADO object but i just like to know is there any other way to do the same thing without using ADO object. Fine no issues, i will go towards the existing way

Stefan said...

I recommend using a real database instead of Excel for better performance. Using ADO with Excel could work but you have to look out for issues with data types and performance when handling large amounts of data.

Syed said...

ok and Do you have any experience working with Terminal Emulator along with QTP?
Currently we are running the batch files manually developed using Mainframe application
We will have to automate those batch files using QTP supported by Terminal Emulator add-ins. I really do not have any idea about this Terminal Emulator and Batch files even. If you have any piece of code as you published for web service testing which was very useful, please designate a new blog on Terminal Emulator which would be very useful. Sorry for bothering you many times. Since i am new to this Web Service and Mainframe testing there is no one to guide me.

Stefan said...

I have worked with the TE add-in some years ago at a former employer...

But my knowledge about it is limited I'm afraid so I recommend you post your request at AdvancedQTP or SQAForums

aswak said...

hi Stefan,
I have tried your code and it works fine.
I have another case where i need to send the xml in the XSD , (XML Schema Definition), so that web service can respond.

can i have the xml files in the simple format and have in it a reference to a XML Schema.

Please let me know your thoughts.
Thanks,
Aswak

Stefan said...

Hi,

My examples are based on XML templates and if your Web Service has schema references just make sure that they are present in the templates.

/Stefan

Syed said...

Hi Stefan,

1) In our web service testing there is no way i could use the Post URL directly. They are not providing the URL and SOAP Action as well.Could u pls suggest an alternative way. Lemme explain the manual process completely so that you could understand how they are currently doing the manual testing.

It has had been handled manually by entering the inputs in a GUI and click execute would generate an input XML internally and sends HTTP requests and produces an Output XML. Then testers used to compare with the expected results manually. As it is taking more time, they wanted it to be automated. Each web service holds more than 50 inputs and generates and output of more than 700 fields.
Can u pls suggest on how to automate such a big web services???

And also under a root element there are lots of child elements where we need to take some inputs from each of the child elements and also if there is a need then we have to get in depth to grandchild and has to get the input. In this case, how we need to fetch. Do you have a Vbscript code which works dynamically for any XML to read all the childs and even if it has grand child then it should go ahead and read it with all the possible checks of whether it has an element.
I beleive am not confusing you... Really i donno how am going to automate such a big web services. I think it would take more time for you to understand but please.....

Stefan said...

Hi,

"...It has had been handled manually by entering the inputs in a GUI"

To automate this step you could use QTP or similar tool.

"...Do you have a Vbscript code which works dynamically for any XML"

It is hard to use generic code if your XML is complex and dynamic...so I often create tailor-made functions if that is the case using XML DOM and XPath.

I strongly recommend you learn a lot about XML DOM and XPath. You find examples and tutorials on W3schools.

Good luck!

/Stefan

Sam said...

Hi Stefan, just came accross this little tool and it looks like a great start for a project of mine!!

Question for you, how would i go about removing the username/password fields from the page? the web service im connecting to doesn't need them and i want to start with the basics.

thanks and awesome stuff here!!

Stefan said...

Hi,

Try to alter the lines regarding SetCredentials:

'SetCredtials if username is provided
If form.username.Value <> "" Then
WinHttpReq.SetCredentials form.username.Value, form.password.Value, _
HTTPREQUEST_SETCREDENTIALS_FOR_SERVER
End If

Good luck!

/Stefan

vikram said...

Hello Stefan,

First of all thank you for all your helpful posts.

I have been trying to use both WinHttpRequest and webservice add-in for qtp for soap request that requires password to be of type - digest. Also I get a response with content-type: multipart/related

When using web service add-in I get a response that I'm looking for but QTP errors out because it cannot recognize multipart/related

With WinHttpRequest I can't figure out how to use password digest.

Any help on this will be greatly appreciated.

vikram said...

Here is sample code using WinHttp

Set httpReq = CreateObject("WinHttp.WinHttpRequest.5.1")
httpReq.Open "POST", "serviceurl", False
httpReq.SetRequestHeader "Content-Type", "text/xml;charset=UTF-8"
httpReq.SetRequestHeader "SOAPAction", "''"
httpReq.SetRequestHeader "Accept", "text/xml, multipart/related, text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2"
httpReq.SetCredentials "user", "password", 0
httpReq.SetAutoLogonPolicy 0
httpReq.SetTimeouts 30000, 30000, 30000, 60000

Set inputXmlDoc = CreateObject("Msxml2.DOMDocument")
inputXmlDoc.Load("tran_batchrequest.xml")
request = inputXmlDoc.xml
print request
httpReq.Send request

xmlResponse = httpReq.ResponseText

print xmlResponse

Stefan said...

Hi,

Unfortunatly I have not encounter Digest authentication yet but perhaps you get some clues here http://msdn.microsoft.com/en-us/library/aa384100(VS.85).aspx

Regards

/Stefan

Shahid said...

Hi Stephan,

In out environment we send request to our web service and as a result our web service send 4 requests to another web service. Is there any way I can verify all four responses which is basically the input for other webservice ?

Thanks
Kashan

software test consultant said...

Hey Stefan, Thank you for publishing your code man, It helped me a lot in my work..saved me a lot of time and effort, I'm sure that I will use your tips once again.