Friday 18 September 2015

Exploring the custom code feature in HP Service Test


This post will help users to customize their REST API Automation tests for XML responses and add their own logic in theirs tests by adding event listeners.You will know how to set the output variable's value through your code and then display custom messages in the reports based on their values set in the custom code.


API Automation is something I have been interested in exploring in the last three months, since then I have been on a hunt for API Automation tools.
I came across HP Service Test (version 12.02) , frankly speaking, I dint go through much of the tool features since i had a little different requirement , so in case you have stopped by my blog to find any tool-specific functionality , please stop reading further now! :-D  Jokes apart, this blog is for people who want to customize their API tests using HP Service Test’s custom code functionality.

What kind of requirement was it that made me dig into Custom Code feature and get my hands dirty with coding?

The requirement which instigated this was – “Verify if the REST API's xml response is sorted”
(I am in process of coding for a JSON response, will update once I am done!)

For e.g.  – Verify if this response is sorted as per “Flight Id” in the demo flight application API - http://localhost:8000/HPFlights_REST/Flights?DepartureCity=Denver&ArrivalCity=London

I am not sure if the tool has any special feature to do so! However, I went ahead to play with this xml response through my code. (You can refer to the code in case you have any such requirement, where you need to dig into the response and do anything with it:-D !)

I assume that you must be familiar with the basic tool usage. However, I am adding screenshots for a little better detailing .  (Note – I have not used all the awesome tool specific features- like adding checkpoints or making the tests data driven etc., this blog is just focused on the coding part.)
This was the first time I was doing something in C# (thanks to a friend Harshad for the help:-D)

Now , we will see how to check if the flight details received are in sorted order :
(make sure to keep the Flight application API running.)

1.    Goto File-> New-> Test->API test



2.     Click on “Add REST Service”.  And enter the base URL : http://localhost:8000/HPFlights_REST





3.     Adding the method : Click on the "Add Method" (plus sign)  button,add the “getFlightDetails” method and mention the relative URL - "/Flights?DepartureCity=Denver&ArrivalCity=London"
(I have just hardcoded it as of now)




4.      Drag and Drop the “getFlightDetails”.
Click on “+ADD”. Add two output properties :”ResponseBody” and “ResponseSorted”































5.   Now, comes an important step where we set the listeners, we know that we have to get the response code once the API is executed. Hence we need to add an “AfterExecuteStepEvent” listener as follows:

                            
6.   You will land on this page to allow you to write your custom code:





  7.       This is my TestUserCode.cs file
(Detailed explanation after code)

    using System.Collections.Generic;
    using System.ComponentModel;
    using System.IO;
    using System.Xml.Serialization;
    using HP.ST.Shared.Utilities.Extensions;
    using System.Linq;
    namespace Script
    {
    using System;
    using System.Xml;
    using System.Xml.Schema;
    using HP.ST.Ext.BasicActivities;
    using HP.ST.Fwk.RunTimeFWK;
    using HP.ST.Fwk.RunTimeFWK.ActivityFWK;
    using HP.ST.Fwk.RunTimeFWK.Utilities;
    using HP.ST.Fwk.RunTimeFWK.CompositeActivities;
    using HP.ST.Ext.CustomDataProviders.Extensions;
    using HP.ST.Ext.CustomDataProviders.ExcelFileArguments;
                                   
        [Serializable()]
        public class TestUserCode : TestEntities
        {
        
            /// <summary>
            /// Handler for the RESTActivityV25 Activity’s AfterExecuteStepEvent event.
            /// </summary>
            /// <param name=\"sender\">The activity object that raised the AfterExecuteStepEvent event.</param>
            /// <param name=\"args\">The event arguments passed to the activity.</param>
            /// Use this.RESTActivityV25 to access the RESTActivityV25 Activity's context, including input and output properties.
            public void RESTActivityV25_OnAfterExecuteStepEvent(object sender, STActivityBaseEventArgs args)
            {
                this.RESTActivityV25.Output.responseBody=this.RESTActivityV25.ResponseBody;

                String xml=this.RESTActivityV25.XMLResponse.InnerXml;     
                XmlReader xReader = XmlReader.Create(new StringReader(xml));
                List < Flight > flightListObj = new List < Flight > ();
                Flight flightObj = null;                                        
                while (xReader.Read())
                 {
                     if(xReader.NodeType == XmlNodeType.Element)
                    {
                        if(xReader.Name.Equals("Airline"))
                        {
                            flightObj=new Flight();
                            xReader.Read();
                            flightObj.SetAirline(xReader.Value);
                        }
                        if(xReader.Name.Equals("ArrivalCity"))
                        {
                            xReader.Read();
                            flightObj.SetArrivalCity(xReader.Value);
                        }
                        if(xReader.Name.Equals("ArrivalTime"))
                        {
                                                               xReader.Read();
                                                               flightObj.SetArrivalTime(xReader.Value);
                                                           }
                                                           if(xReader.Name.Equals("DepartureCity"))
                                                           {
                                                               xReader.Read();
                                                               flightObj.SetDepartureCity(xReader.Value);
                                                           }
                                                           if(xReader.Name.Equals("DepartureTime"))
                                                           {
                                                               xReader.Read();
                                                               flightObj.SetDepartureTime(xReader.Value);
                                                           }
                                                           if(xReader.Name.Equals("FlightNumber"))
                                                           {
                                                               xReader.Read();
                                                               flightObj.SetFlightNumber(xReader.Value);
                                                           }
                                                           if(xReader.Name.Equals("Price"))
                                                           {
                                                               xReader.Read();
                                                               flightObj.SetPrice(xReader.Value);
                                                               flightListObj.Add(flightObj);
                                                            flightObj=null;                                                                    
                                                           }
                                                       
                                                       }
                                                }
                                                 
                                                   checkSortedFlightNumber(flightListObj);
                                                   if(checkSortedFlightNumber(flightListObj))
                                                         this.RESTActivityV25.Output.responseSorted="Sorted in ascending order of Flight Numbers";
                                                else
                                                        this.RESTActivityV25.Output.responseSorted="NOT Sorted in ascending order of Flight Numbers";
                                        }
                                            public static bool checkSortedFlightNumber(List<Flight> flightListObj)
                                            {
                                                int[] flightNumbers = new int[flightListObj.Count];
                                                int counter=0;
                                                foreach (Flight flightObj in flightListObj)
                                                    {
                                                        flightNumbers[counter] = int.Parse(flightObj.getFlightNumber());
                                                        counter++;                                                
                                                }
                                                if(IsSorted(flightNumbers))
                                                    return true;

                                                else
                                                    return false;

                                            }
                                            public static bool IsSorted(int[] arr)
                                            {
                                                for (int i = 1; i < arr.Length; i++)
                                                    {
                                                        if (arr[i - 1] > arr[i])
                                                        {
                                                            return false;
                                                        }
                                                    }
                                                        return true;
                                                    }
                                            
                                        }
                                
                                        public class Flight
                                        {
                                            public String airline;
                                            public String arrivalCity;
                                            public String arrivalTime;
                                            public String departureCity;
                                            public String departureTime;
                                            public String flightNumber;
                                            public String Price;
                                        
                                            public void SetAirline(String airline)
                                            {
                                                this.airline=airline;
                                            }
                                            public String getAirline()
                                            {
                                                return airline;
                                            }
                                            public void SetArrivalCity(String arrivalCity)
                                            {
                                                this.arrivalCity=arrivalCity;
                                            }
                                            public String getArrivalCity()
                                            {
                                                 return arrivalCity;
                                            }
                                            public void SetArrivalTime(String arrivalTime)
                                            {
                                                this.arrivalTime=arrivalTime;
                                            }
                                            public String getArrivalTime()
                                            {
                                                return arrivalTime;
                                            }
                                            public void SetDepartureCity(String departureCity)
                                            {
                                                this.departureCity=departureCity;
                                            }
                                            public String getDepartureCity()
                                            {
                                                return departureCity;
                                            }
                                            public void SetDepartureTime(String departureTime)
                                            {
                                                this.departureTime=departureTime;
                                            }
                                            public String getDepartureTime()
                                            {
                                                return departureTime;
                                            }
                                            public void SetFlightNumber(String flightNumber)
                                            {
                                                this.flightNumber=flightNumber;
                                            }
                                            public String getFlightNumber()
                                            {
                                                return flightNumber;
                                            }
                                            public void SetPrice(String Price)
                                            {
                                                this.Price=Price;
                                            }
                                            public String getPrice()
                                            {
                                                return Price;
                                            }
                                        
                                        }
                                    
                                    }

   The code details:


a)      We know that once we fire the API , we need to plunge our hands into the code and dig out the Response XML, hence this code should be a part of the “OnAfterExecuteStepEvent”, which means this will be executed after the API is triggered.

b)      We have this listener “  RESTActivityV25_OnAfterExecuteStepEvent”   
“RESTActivityV25” - is the step id of the “getFlightDetails” step.
 “OnAfterExecuteStepEvent” - says that this will be called once the “getFlightDetails” step is executed and we have our Response ready to be attacked!


c)       this.RESTActivityV25.XMLResponse.InnerXml – this gives us the XML response.
(Use this.RESTActivityV25 to access the RESTActivityV25 Activity's context, including input and output properties)
d)      We use the XMLReader class to scan through the xml response and pick out the values of the attributes we need. 

e)      We create a Flight class mapping the XML Response.

f)       We populate Flight class objects with the values we pick while we scan through the xml .Then we create a list of Flight objects. So this list will contain all the Flight details returned by the API. Each flight detail is represented as an object.

g)      Now , to check if these details are sorted in ascending order of “Flight Number”, we scan through each element of the list i.e. each Flight object and then pick the Flight Numbers and dump it in and array ( the function -  checkSortedFlightNumber)

h)      Then the function IsSorted will check if the passed array (array of Flight Numbers) is sorted in ascending order.

i)        The output parameter for “this.RESTActivityV25.Output.responseSorted” is set based on the response of the function - checkSortedFlightNumber.
    if(checkSortedFlightNumber(flightListObj))
             this.RESTActivityV25.Output.responseSorted="Sorted in ascending order";
     else
         this.RESTActivityV25.Output.responseSorted="NOT Sorted in ascending order"

(Note I have another output “responseBody”, this was just for me to write the response xml into a file, it was a part of my experiment to write the response into a file)

j)        This above “responseSorted” String is an input to the next step “Concatenate String” which prints the response in the report.


8.       Now just to have the output printed in my report , I use the “Concatenate String “ function ( check in the toolbox section) and print if the response is in sorted order.
I also dump in the response to a file .

a)      Drag and Drop the “Concatenate String”
b)      Set the “prefix” as “The Response is :”
c)       To set the suffix- Click on the “chain” symbol and select the output parameter –“ResponseSorted” of the “getFlightDetails” step.
This way we can display our customized reporting messages too.





9.       Just for some reason I dumped the output to a text file too.
a)      Drag and Drop the “Write to file” from toolbox
b)      Set the “content” by clicking of the “chain” symbol .
c)       Set it to the output parameter- “ResponseBody” of the “getFlightDetail” step. 
d)      Also provide the  “File Path” – location of your text file , where the response has to be written.





Now run this test.
NOTE : Make sure to load replay otherwise you may get error after running for the line :



                String xml=this.RESTActivityV25.XMLResponse.InnerXml;         




10.   And here’s the report after I run this test :







This is the way I solved the sorting problem!
If you faced a similar challenge and solved it in another way, don’t hesitate to explain it to me!
Thanks for stopping by , hope this helped you! Do drop in your comments!







10 comments:

  1. Hi...

    I am getting the error while i try to run the code. The error is in the .CS file

    "Invalid token 'namespace' in class, struct, or interface member declaration (CS1519) TestUserCode.cs"

    Also while trying to select "load replay " i do get the following error

    "Load failed: No replay results available"

    PS>- I am totally new to UFT and REST service testing!! Appreciate your help.

    ReplyDelete
    Replies
    1. Hi,
      Regarding the load replay error, I think you can start all over again, once u complete step 4, just try running your script and see if you get any response from the API. Once u get the response , then try proceeding with the next steps.
      This should fix the load replay issue.

      Delete
  2. Hi,

    Thank you for the detailed explanation. However i have one question. the API which you explained above is open for all. how to send REST API request for secured domain. ie In REST API Testing, how to pass username/pwd for Basic Authentication.

    please let me know, if the question is not clear.
    thank you.

    ReplyDelete
  3. where to give input to REST service, if I need to give input?

    ReplyDelete
  4. Given rest services are generally secured by OAuth2 tokens, it's dissappointing that uft doesn't provide OAuth2 support in the same way free tools such as postman and soapui do.

    ReplyDelete

  5. I would like to thank you for the efforts you have made in writing this article. good information in this article, thanks .
    Epson projector support
    Epson projector
    Epson projector number
    Epson projector service

    ReplyDelete
  6. Hello,

    In UFT one 15.01 version, i have created a service RestAPI test using import WADL from file.

    The API is hosted on Azure.

    I can see the input parameters and all. But when i fill everything and send the request then i'm getting status code in the 'Run Step Results' windows as '403-Forbidden'. I have also provide the key and its value by adding request header array under activity properties.


    Please suggest.

    ReplyDelete

  7. Nice post! You are sharing amazing information through your blog. I am a big fan of your excellent writing skills. And for more information and technical Support services about Geek Squad Phone Service Number .
    Geek Squad Protection Plan
    Geek Squad Online Support
    Geek squad tech support

    ReplyDelete