Visual Studio 2005 web tests could be effectively used to test the web services too. However there are some key points which one should keep in mind for writing those web service tests. Let us go through them and see what they are:

 

1.     To begin:

We should have a web test project at hand. Right-click on the test, and select “Add Web Service Request” from the context menu.

The URL property of the newly added web service request should point to the asmx file of the web service which you wish to test.

e.g. http://www.w3shools.com/webservices/tempconvert.asmx

 

2.     SOAPAction Header:

The SOAPAction header must be added to the web service request. This is a hint to the servers that the HTTP POST contains SOAP request and the value of the header is the URI indicating the intent of the SOAP request.

e.g. SOAPAction = http://tempuri.org/CelsiusToFahrenheit

 

This could be selecting “Add Header” from the context menu of the web service request. Then set the “Name” property as “SOAPAction” and the value as URI of the web service function to be invoked.

 

3.     String Body:

This is the actual body of the SOAP request and is basically a SOAP envelop which looks something like:

<?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>

    <CelsiusToFahrenheit xmlns="http://tempuri.org/">

      <Celsius>value to be sent</Celsius>

    </CelsiusToFahrenheit>

  </soap:Body>

</soap:Envelope>

 

The Content type of the String body should be kept as: text/xml.

 

NOTE:

  1. This SOAP envelope and the SOAPAction header are dependant on the web service in question and could be easily obtained by visiting the URL of the web service provider.
  2. We may need to send some arguments to the invoked web service, which could be hard coded in to the String body of the web service request or we may intend to use some context parameter for the purpose, which is actually a better way of doing the things as it would aid in:

a.)    Ease of changing the value.

b.)    Data driving the whole web service test.

 

The name of the context parameter should be used inside double braces.

e.g. {{param-name}}

 

4.     Validation Rules:

The pre-canned validation rules provided with the web tests are not good enough for validating the web service tests, as they are designed to work with the HTML documents whereas the web services (SOAP) responses are XML documents. So we have to write our custom validation rules for the web services validation.

Following are the steps to write a custom validation rule:

  1. Derive your new validation rule from the base class ValidationRule.
  2. Provide the read-only RuleName and RuleDescription properties.
  3. All public properties of your validation rule would be available for interaction in the UI for adding validation rules.
  4. Override the Validate method of the base class providing your implementation of validations.
  5. For example, to validate the content of a particular tag in SOAP response we may have validation rule of the sort:

 

    public class CustomValidateTag : ValidationRule

    {

        public override string RuleName

        {

            get { return "Validate TagContent"; }

        }

 

        public override string RuleDescription

        {

            get { return "Validates whether tag in question contains the intended value"; }

        }

 

        private string TagNameValue;

        public string TagName

        {

            get { return TagNameValue; }

            set { TagNameValue = value; }

        }

 

        private string IntendedTagValue;

        public string IntendedValue

        {

            get { return IntendedTagValue; }

            set { IntendedTagValue = value; }

        }

 

        public override void Validate(object sender, ValidationEventArgs e)

        {

            e.IsValid = false;

 

            XmlNodeList nodes  = e.Response.XmlDocument.GetElementsByTagName(TagName);

            if (nodes.Count > 0)

            {

                e.IsValid = true;

 

                foreach (XmlNode node in nodes)

                {

                    if (!string.Equals(node.Value, IntendedValue,

 StringComparison.InvariantCultureIgnoreCase))

                    {

                        e.IsValid = false;

                        break;

                    }

                }

            }

       

            // If the validation fails, set the error text that the user sees

            if (!e.IsValid)

            {

              e.Message = String.Format("{0} Tag does not contain {1}",TagName,IntendedValue);

            }

         }

      }

 

 

  1. Similarly for validating the absence of “fault code” in the SOAP response the validation rule may look like:

 

 public class FaultCodeValidaionRule : ValidationRule

    {

        public override string RuleName

        {

            get { return "Fault Code Validation"; }

        }

 

        public override string RuleDescription

        {

            get { return "Validates if SOAP response contains some fault code"; }

        }

 

        public override void Validate(object sender, ValidationEventArgs e)

        {

            bool validated = true;

 

            XmlNodeList faults = e.Response.XmlDocument.GetElementsByTagName("soap:Fault");

 

            if (faults.Count > 0)

                validated = false;

 

            foreach (XmlNode node in faults)

            {

                e.Message = String.Format("SOAP Error: ");

 

                foreach (XmlNode child in node.ChildNodes)

                {

                      e.Message += String.Format("{0}= {1}, ", child.Name, child.InnerText);

                }

            }

 

            e.IsValid = validated;

        }

  }

 

  1. NOTE: while writing validation rules for the SOAP responses we should working with the XML Document in the response object and not the HTML Document, as that would be null.

 

 

 

5.     Extraction Rules:

On the parallel line we shall create custom extraction rules for the web service tests. That would require extending ExtractionRule base class, providing the RuleName, RuleDescription read-only public properties, and overriding the Extract method.

Here is an example extraction rule which evaluates the given XPath query against the SOAP response.

 

    public class EvaluateXPathQuery : ExtractionRule

    {

        public override string RuleName

        {

            get { return "Evaluate XPath Query"; }

        }

 

        public override string RuleDescription

        {

            get { return "Evaluates the given XPath Query"; }

        }

 

        private string XPathQueryValue;

        public string XPathQuery

        {

            get { return XPathQueryValue; }

            set { XPathQueryValue = value; }

        }

 

        public override void Extract(object sender, ExtractionEventArgs e)

        {

            XmlNamespaceManager nsManager

 = new XmlNamespaceManager(e.Response.XmlDocument.NameTable);

            nsManager.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");

 

            XPathNavigator navigator = e.Response.XmlDocument.CreateNavigator();

            XmlNode body = e.Response.XmlDocument.SelectSingleNode("//soap:Body", nsManager);

           

            // Very first child of soap response envelope's body tag is the response tag.

            string responseNameSpace = body.FirstChild.Attributes["xmlns"].Value;

            if (responseNameSpace != null && responseNameSpace.Length > 0)

            {

                nsManager.AddNamespace("response", responseNameSpace);

 

                // Now prefix temp namespace for every node in the query.

                int index = 0;

                while ((index = XPathQuery.IndexOf('/', index)) != -1)

                {

                    if (XPathQuery[index + 1] == '/')

                    {

                        index++;

                    }

                   

                    if (!XPathQuery.Substring(index + 1, 4).Equals("soap",

 StringComparison.InvariantCultureIgnoreCase))

                    {

                        XPathQuery = XPathQuery.Insert(index + 1, "response:");

                    }

                    ++index;

                }

            }

 

            XPathNodeIterator iterator

 = navigator.Evaluate(XPathQuery, nsManager) as XPathNodeIterator;

 

            if (iterator.Count > 0)

            {

                string val = string.Empty;

                while (iterator.MoveNext())

                {

                    val += String.Format("{0}: {1}", iterator.Current.Name,

iterator.Current.Value);

                }

                e.Success = true;

                e.WebTest.Context.Add(this.ContextParameterName, val);

            }

            else

            {

                e.Success = false;

                e.Message = String.Format(CultureInfo.CurrentCulture,

"No result for query: {0}", XPathQuery);

            }

           

            return;

        }

 }

 

6.     IMPORTANT:

XPath expressions do not have the concept of a default namespace.  Omitting the prefix from an XPath looks for nodes in the empty namespace, and finds nothing. So we must use an arbitrary prefix for xml namespace in the SOAP response envelope. This namespace is attribute of the Response tag, which is the first child of the soap:Body tag.

 

7.     XPath Query Tools:

http://www.stylusstudio.com/xpath_editor.html

http://www.freewarefiles.com/program_4_50_14082.html

http://www.topxml.com/xselerator/