VSTS 2010 Feature: Extensible recorder plugins for modifying recorded web tests (including adding custom dynamic parameter correlation)

This is another post about new VS 2010 web/load testing features. You can get the beta here: beta download.

In this post I am going to talk about a new feature that can help with web test recording. The feature is extensible recorder plug-ins for modifying recorded web tests. Basically we are giving you the opportunity to modify the recorded web test after you click stop on the web test recorder bar but prior to the web test being fully saved back to the web test editor. So what problems does this help with? The main one is performing your own custom correlation. In VS 2008 we added a process which runs post recording that attempts to find dynamic fields. You can read this blog post for more information: https://blogs.msdn.com/slumley/pages/web-test-correlation-helper-feature-in-orcas.aspx

This process still exists, but this process does not always find all dynamic fields for an application. So if we did not find the dynamic fields in your application you had to manually perform the correlation process. Here is a blog post that goes into detail about the manual process: https://blogs.msdn.com/slumley/pages/how-to-debug-a-web-test.aspx Also there are cases that our correlation process does not find the dynamic values, such as dynamic values in the URL.

At a high level, you have to:

1) Determine what parameters are dynamic

2) Then for each parameter find the first occurrence of this in a response body.

3) Add an extraction rule to pull the value out of the response and add it to the context

4) Then modify each query string or form post parameter that needs this value by changing the value to pull the value out of the context.

This new feature allows you to write your own plug-in which can perform correlation or modify the web test in many ways prior to it being saved back to the web test editor. So once you figure out that certain dynamic variable have to be correlated for each of your recordings, you can automate the process. To demonstrate how this works, I am going to write a recorder plug-in which will perform the correlation that I manually walked through in my previous post. Please quickly read that: https://blogs.msdn.com/slumley/pages/vs-2010-feature-web-test-playback-enhancements.aspx

Overview

Create the plug-in

Recorder plug-ins follow the same pattern as WebTestPlugins or WebTestRequestPlugins. To create a plug-in, you will create a class that extends WebTestRecorderPlugin and then override the PostWebTestRecording method:

public class Class1 : WebTestRecorderPlugin

    {

        public override void PostWebTestRecording(object sender, PostWebTestRecordingEventArgs e)

        {

            base.PostWebTestRecording(sender, e);

        }

    }

Modify the web test

The event args will give you 2 main objects to work with: the recorded result and the recorded web test. This will allow you to iterate through the result looking for certain values and then jump to the same request in the web test to make modifications. You can also just modify the web test if you wanted to add a context parameter or maybe parameterize parts of the URL. If you do modify the web test, you also need to set the ReocrdedWebTestModified property to true. e.RecordedWebTestModified = true;

Deploy the plug-in

After compiling the plug-in, you will need to place the dll in 1 of 2 spots:

1) Program Files\Microsoft Visual Studio 10.0\Common7\IDE\PrivateAssemblies\WebTestRecorderPlugins

2) %USERPROFILE%\My Documents\Visual Studio 10\WebTestRecorderPlugins

Executing the plug-in

After you deploy the plug-in, you will need to restart VS for the plug-in to be picked up. Now when you create a web test, you will see a new dialog. The dialog will display all of the available plug-ins that can be executed. Select your plug-in and hit ok. Once you are done recording your web test, the plug-in will be executed.

Creating the Sample Plug-in

First a quick review of the correlation that we are going to automate. Here is the screen shot from correlation tool after I recorded my web test against a reporting services site.

 

 

We are going to correlate the ReportSession parameter.

1) Create a class library project

2) Right click references and select Add Reference

3) Choose Microsoft.VisualStudio.QualityTools.WebTestFramework

4)  Here is the code for my plug-in:

using System.ComponentModel;

using Microsoft.VisualStudio.TestTools.WebTesting;

using Microsoft.VisualStudio.TestTools.WebTesting.Rules;

namespace RecorderPlugins

{

    [DisplayName("Correlate ReportSession")]

    [Description("Adds extraction rule for Report Session and binds this to querystring parameters that use ReportSession")]

    public class CorrelateSessionId : WebTestRecorderPlugin

    {

        public override void PostWebTestRecording(object sender, PostWebTestRecordingEventArgs e)

        {

            //first find the session id

            bool foundId = false;

            foreach (WebTestResultUnit unit in e.RecordedWebTestResult.Children)

            {

                WebTestResultPage page = unit as WebTestResultPage;

                if (page != null)

                {

                    if (!foundId)

                    {

                        int indexOfReportSession = page.RequestResult.Response.BodyString.IndexOf("ReportSession");

                        if (indexOfReportSession > -1)

                        {

                            //add an extraction rule to this request

                     // Get the corresponding request in the Declarative Web test

                            ExtractionRuleReference ruleReference = new ExtractionRuleReference();

                            ruleReference.Type = typeof(ExtractText);

                  ruleReference.ContextParameterName = "SessionId";

                            ruleReference.Properties.Add(new PluginOrRuleProperty("EndsWith", "&ControlID="));

                            ruleReference.Properties.Add(new PluginOrRuleProperty("HtmlDecode", "True"));

                            ruleReference.Properties.Add(new PluginOrRuleProperty("IgnoreCase", "True"));

                            ruleReference.Properties.Add(new PluginOrRuleProperty("Index", "0"));

                            ruleReference.Properties.Add(new PluginOrRuleProperty("Required", "True"));

                            ruleReference.Properties.Add(new PluginOrRuleProperty("StartsWith", "ReportSession="));

                            ruleReference.Properties.Add(new PluginOrRuleProperty("UseRegularExpression", "False"));

                            WebTestRequest requestInWebTest = e.RecordedWebTest.GetItem(page.DeclarativeWebTestItemId) as WebTestRequest;

                            if (requestInWebTest != null)

                            {

                                requestInWebTest.ExtractionRuleReferences.Add(ruleReference);

                                e.RecordedWebTestModified = true;

                            }

              foundId = true;

                        }

                    }

                    else

                    {

                        //now update query string parameters

                        WebTestRequest requestInWebTest = e.RecordedWebTest.GetItem(page.DeclarativeWebTestItemId) as WebTestRequest;

                        if (requestInWebTest != null)

                        {

                            foreach (QueryStringParameter param in requestInWebTest.QueryStringParameters)

                            {

                                if (param.Name.Equals("ReportSession"))

                                {

                                    param.Value = "{{SessionId}}";

                          }

                            }

                        }

                    }

                }

            }

        }

    }

}

5) Let’s review parts of the class.

a. Iterate through the result to find first page with ReportSession. This code fragment iterates through each of the recorded objects and searches the response body for ReportSession.

           foreach (WebTestResultUnit unit in e.RecordedWebTestResult.Children)

            {

                WebTestResultPage page = unit as WebTestResultPage;

                if (page != null)

                {

                  if (!foundId)

                    {

                        int indexOfReportSession = page.RequestResult.Response.BodyString.IndexOf("ReportSession");

                        if (indexOfReportSession > -1)

                        {

                        …

b. Now that we found the response, we need to add an extraction rule. This code creates the extraction rule and then finds the correct request in the web test to add the extraction rule to. Each result object has a property called DeclaraticveWebTestItemId which is what we will use to get correct request from the web test.

           ExtractionRuleReference ruleReference = new ExtractionRuleReference();

           ruleReference.Type = typeof(ExtractText);

           ruleReference.ContextParameterName = "SessionId";

           ruleReference.Properties.Add(new PluginOrRuleProperty("EndsWith", "&ControlID="));

           ruleReference.Properties.Add(new PluginOrRuleProperty("HtmlDecode", "True"));

           ruleReference.Properties.Add(new PluginOrRuleProperty("IgnoreCase", "True"));

           ruleReference.Properties.Add(new PluginOrRuleProperty("Index", "0"));

           ruleReference.Properties.Add(new PluginOrRuleProperty("Required", "True"));

           ruleReference.Properties.Add(new PluginOrRuleProperty("StartsWith", "ReportSession="));

           ruleReference.Properties.Add(new PluginOrRuleProperty("UseRegularExpression", "False"));

           WebTestRequest requestInWebTest = e.RecordedWebTest.GetItem(page.DeclarativeWebTestItemId) as WebTestRequest;

           if (requestInWebTest != null)

           {

            requestInWebTest.ExtractionRuleReferences.Add(ruleReference);

               e.RecordedWebTestModified = true;

           }

c. Now we need to find all query string parameters that have ReportSession as name and change the value to {{SessionId}}

           WebTestRequest requestInWebTest = e.RecordedWebTest.GetItem(page.DeclarativeWebTestItemId) as WebTestRequest;

           if (requestInWebTest != null)

           {

               foreach (QueryStringParameter param in requestInWebTest.QueryStringParameters)

               {

                    if (param.Name.Equals("ReportSession"))

                    {

                        param.Value = "{{SessionId}}";

                    }

                }

            }

6) Now that we have our plug-in, I need to compile and deploy it to one of the locations listed above.

7) Restart VS

8) Open a test project and create a new web test. I now see the following dialog with my plug-in available:

 

 

9) Select the plug-in

10) Record the same web test against my reporting services site and click stop to end the web test.

11) Now when the correlation process runs, you will see that it does not find the ReportSession parameter. This is because we have already correlated it.

 

 

12) Now look at the first request in the web test and you will see the extraction rule.

 

 

13) Now look at the other requests to see where we are referencing the extraction rule.

 

 

This is a slightly more advanced feature, but it provides a huge time savings for automating changes to your recorded web test. If you have multiple people creating web tests, you can use this plug-in to make sure the same parameters or rules are added to each web test. And of course you can automate correlation of parameters or URLs which the built in correlation tool does not find.