Extracting all instances of a specific type of Names & Values from web test response using regular expressions

Extract all the values highlighted as blue below which are part of the response stream. And then select some of them randomly and post with the requests as appropriate.

Right now Visual Studio extraction rule let you extract only one text value with “Starts With” and “Ends With” parameters which is not really very helpful in this scenario.

Especially if I want to extract all the name value pairs, the situation is little different and complex.

Take the example below. I want to capture all the name/values as highlighted below and tore it in an array in the test context for later use.

<tr bgColor="#FFFFFF" onMouseOver="this.bgColor='#CECECE';" onMouseOut="this.bgColor='#FFFFFF';" Class="Standard">

<td valign="top"> <input Type="checkbox" ID="chk_4" Name="chk_4" Value="b3b30af17536451dad3962a097a5ea4d" onClick="changeInstrumentStatus('chk_4');">

</td>

<td valign="top">

<img ID="img_4" className="Collapsed" SRC="images/plus.gif" onClick="loadItems('b3b30af17536451dad3962a097a5ea4d');switchPlusMinus('child_4','img_4');">

</td>

I am not going through the very basics of how to extend a Extraction rule framework and add it to a web test project. You can read  my colleague Amol's blog here.

Depending on the requirement the extracted values can be added to an array list or a dictionary for Key Value lookup. This example uses a Dictionary data structure.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.WebTesting;
using System.Text.RegularExpressions;

namespace MyVSTSRuleLib
{

    public class ExtractNameValPair : ExtractionRule
    {
         // The name of the desired input field

        private string NTxtStart;
        public string NameStartsWith
        {
            get { return NTxtStart; }
            set { NTxtStart = value; }
        }

        private string NTxtEnd;
        public string NameEndsWith
        {
            get { return NTxtEnd; }
            set { NTxtEnd = value; }
        }

        private string VTxtStart;
        public string ValueStartsWith
        {
            get { return VTxtStart; }
            set { VTxtStart = value; }
        }

        private string VTxtEnd;
        public string ValueEndsWith
        {
            get { return VTxtEnd; }
            set { VTxtEnd = value; }
        }

        public override void Extract(object sender, ExtractionEventArgs e)
        {
            if (e.Response.HtmlDocument != null)
            {
                int startPos = 0;
                int currentPos = 0;

                Dictionary<string, string> NVPairs = new Dictionary<string, string>();

           //List<string> foundItems = new List<string>();

                Regex rgNTxtStart = new Regex(NameStartsWith);
                Regex rgNTxtEnd = new Regex(NameEndsWith);
                Regex rgVTxtStart = new Regex(ValueStartsWith);
                Regex rgVTxtEnd = new Regex(ValueEndsWith);

                while (true)
                {

                    Match nStarts = rgNTxtStart.Match(e.Response.BodyString, startPos);
                    if (!nStarts.Success)
                    {
                        break;
                    }

                    currentPos = nStarts.Index + nStarts.Length;
                    startPos = currentPos;

                    Match nEnds = rgNTxtEnd.Match(e.Response.BodyString, startPos);
                    if (!nEnds.Success)
                    {
                        break;
                    }

                    startPos = nEnds.Index + nEnds.Length;

                    string name = e.Response.BodyString.Substring(currentPos, (nEnds.Index - currentPos));

                    //-----------------------------------

                    Match vStarts = rgVTxtStart.Match(e.Response.BodyString, startPos);
                    if (!vStarts.Success)
                    {
                        break;
                    }

                    currentPos = vStarts.Index + vStarts.Length;
                    startPos = currentPos;

                    Match vEnds = rgVTxtEnd.Match(e.Response.BodyString, startPos);
                    if (!vEnds.Success)
                    {
                        break;
                    }

                    startPos = vEnds.Index + vEnds.Length;

                    string value = e.Response.BodyString.Substring(currentPos, (vEnds.Index - currentPos));

                    NVPairs.Add(name, value);

                }

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

            }

        }

    }
}

If you add this to your VSTS project you will find a new Extraction rule named “ExtractNameValPair’ as highlighted below. Put your boundaries and you are good to go . All the extracted values will be  available in the test context.

image

Now another challenge was to capture b3b30af17536451dad3962a097a5ea4d from the pattern below figuring out the StartsWith parameter.

input Type="checkbox" ID="chk_4" Name="chk_4" Value="b3b30af17536451dad3962a097a5ea4d"

Because chk_4 can be chk_40 or chk_100 depending on the page response. So the best solution is to use regular expression as the start parameter. I used “chk_ [0-9]+ " Value=" , because the regex  [0-9]+ pattern will take care of all such considerations and the rule above supports it.