Writing Fiddler Web Test Plugins
My last blog post discussed changes made to fiddler which added enhanced support for saving web tests. This post will go into more detail about the changes and show you how to write your own custom code which can modify the way a web test is saved.
The main change we made for saving web tests was adding a plug-in architecture which is very similar to Web Test Plug-ins and Web Test Request plug-ins. The new architecture provides you with a hook into the web test save code so that you can modify what is written to the web test. Each of the new filter type features listed in my previous post were written by writing FiddlerWebTest plug-ins. For example, the FilterByExtension plugin loops through each request that would be written to the web test and flags certain requests to not be written based on certain extensions.
Let’s walk through the process of writing a new plugin. We will write a plugin which will automatically correlate dynamic fields associated with a SQL Server Reporting services site. This is the same reporting site I blogged about in this post: Debugging a Web Test. There are 2 dynamic fields which always need to be correlated for the reporting site that I am testing. They are the ExecutionID and the ControlID. By correlated, I mean that we need to the following:
1. Add an extraction rule to the first request to pull out the values for these fields for the current session
2. Then each time a subsequent request uses these parameters, bind them to the values extracted in the first step.
Since I know that I need to fix up every test I write, wouldn’t it be nice to write a plug-in which will automatically do this for me at save time. Let’s walk through this process.
1. First install the new version of fiddler if you have not done so already: https://www.fiddler2.com/dl/Fiddler2Setup.exe
2. Open Visual Studio and create a library project.
3. Add Fiddler.exe as a reference to the library project. This is typically installed in the following location: C:\Program Files\Fiddler2
4. Now we are ready to start writing our plugin. Add a class to the project and give it a name indicating what the plugin will do. I will call mine ReportingServicesPlugin.
5. Add a using statement to the plugin for Fiddler.WebTesting and Fiddler;
6. Have your class implement the IFiddlerWebTestPlugin interface
7. After making these changes you class should look like the following:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Fiddler;
using Fiddler.WebTesting;
namespace FiddlerPluginExample
{
public class ReportingServicesPlugin : IFiddlerWebTestPlugin
{
#region IFiddlerWebTestPlugin Members
public void PreWebTestSave(object sender, PreWebTestSaveEventArgs e)
{
}
#endregion
}
}
As you can see the IFiddlerWebTestPlugin interface has one method that needs to be implemented, PreWebTestSave. This method will be called prior to the test being saved and the event args passed in has a reference to the fiddler sessions being saved.
8. Now we need to figure out what our plugin needs to do. This plugin needs to do the following:
a. Figure out which request has the ExecutionID and ControlID parameters.
b. Add extraction rules to this request for these parameters
c. Then scan remaining requests and replace the hard coded values with the extracted value.
9. So let’s record the script using fiddler and see what we have. After recording the script, Fiddler has the following:
10. As you can see from the highlighted section on the right, I found the 2 parameters that I am looking for. How does this translate to the plugin. We can simply scan the responses looking for these values in the response. Once we find the correct request, we can add extraction rules. After adding this code, our class would look like the following:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Fiddler.WebTesting;
using Fiddler;
namespace FiddlerPluginExample
{
public class ReportingServicesPlugin : IFiddlerWebTestPlugin
{
#region IFiddlerWebTestPlugin Members
public void PreWebTestSave(object sender, PreWebTestSaveEventArgs e)
{
//loop through each request and try to find the ExecutionID
//and ControlID parameters
for (int i = 0; i < e.FiddlerWebTest.Sessions.Count; i++)
{
//Get the WebTestSession which is a wrapper around the
//Fiddler Session Object
WebTestSession session = e.FiddlerWebTest.Sessions[i];
//If the fiddler session is not null and has a response
//then let's search the response for ControlID and ExecutionID
if (session.FiddlerSession != null
&& session.FiddlerSession.responseBodyBytes != null)
{
//get the response body
session.FiddlerSession.utilDecodeResponse();
string responseString = CONFIG.oHeaderEncoding.GetString(session.FiddlerSession.responseBodyBytes).Trim();
//Look for our 2 parameters
if (responseString.Contains("ExecutionID=")
&& responseString.Contains("ControlID="))
{
//we have found the correct request,
//now let's add a ExtractionRule for each parameter
}
}
}
#endregion
}
}
}
11. The next thing we need to do is add an extraction rule for each parameter. I am going to add an ExtractText rule. This code will go into the last if in the code above. This code looks like the following. You can see I call a method to create an instance of the rule and then add the rule to the extraction rule collection for the session.:
//first the ExecutionID
session.ExtractionRules.Add(CreateExtractionRule("ExecutionID"));
//Now the ControlID
session.ExtractionRules.Add(CreateExtractionRule("ControlID"));
12. I added a method called CreateExtractionRule which looks like the following. :
private static ExtractionRule CreateExtractionRule(string Param)
{
//create the rule
ExtractionRule rule = new ExtractionRule(ExtractionRule.ExtractTextClassName);
rule.ContextParameterName = Param;
//add the rule properties
rule.Properties.Add(new RuleProperty(ExtractionRule.StartsWith, Param + "="));
rule.Properties.Add(new RuleProperty(ExtractionRule.EndsWith, "&"));
rule.Properties.Add(new RuleProperty(ExtractionRule.IgnoreCase, "True"));
rule.Properties.Add(new RuleProperty(ExtractionRule.UseRegularExpression, "False"));
rule.Properties.Add(new RuleProperty(ExtractionRule.Required, "True"));
rule.Properties.Add(new RuleProperty(ExtractionRule.Index, "0"));
return rule;
}
13. Now that we have added the rule, we need to loop through the remaining requests looking for query string whose names are ExecutionID or ControlID. If we find one, then we need to replace the hard coded value with the value from the extraction rule. This looks like the following:
if (foundParameters && session.FiddlerSession != null)
{
foreach (QueryStringParameter qsp in session.RequestQueryParams)
{
if (qsp.Name.Equals("ExecutionID"))
{
qsp.Value = "{{ExecutionID}}";
}
else if (qsp.Name.Equals("ControlID"))
{
qsp.Value = "{{ControlID}}";
}
}
}
14. Now compile and if it builds, then the plug-in is ready for use.
15. Fiddler looks in the following location for classes that implement the IFiddlerWebTestPlugin interface: c:\My Documents\Fiddler2\Scripts\vswebtest
16. Copy your dll to this location.
17. Now we are ready to save the test. First let’s save the test without using this new plug-in. When we do that, the output is the following. Notice there are no extraction rules and the parameters are still hard coded:
18. Now let’s use the new plug-in. The first thing to notice is that the new plug-in appears in the Select Plugin dialog:
19. Open the saved test in VS and it will look like the following. Notice that the test already has the correct extraction rules and that the query string parameters are set to pull from the extracted value instead of being hardcoded. This is test is ready to run without any modifications:
Hopefully this example shows you how you can modify the way a web test is saved so that you can avoid having to do the same fix up tasks for all of the web tests for your site.
Here is the full code example:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Fiddler.WebTesting;
using Fiddler;
namespace FiddlerPluginExample
{
public class ReportingServicesPlugin : IFiddlerWebTestPlugin
{
#region IFiddlerWebTestPlugin Members
public void PreWebTestSave(object sender, PreWebTestSaveEventArgs e)
{
//loop through each request and try to find the ExecutionID
//and ControlID parameters
bool foundParameters = false;
for (int i = 0; i < e.FiddlerWebTest.Sessions.Count; i++)
{
//Get the WebTestSession which is a wrapper around the
//Fiddler Session Object
WebTestSession session = e.FiddlerWebTest.Sessions[i];
//If the fiddler session is not null and has a response
//then let's search the response for ControlID and ExecutionID
if (!foundParameters
&& session.FiddlerSession != null
&& session.FiddlerSession.responseBodyBytes != null)
{
//get the response body
session.FiddlerSession.utilDecodeResponse();
string responseString = CONFIG.oHeaderEncoding.GetString(session.FiddlerSession.responseBodyBytes).Trim();
//Look for our 2 parameters
if (responseString.Contains("ExecutionID=")
&& responseString.Contains("ControlID="))
{
//we have found the correct request,
//now let's add a ExtractionRule for each parameter
foundParameters = true;
//first the ExecutionID
session.ExtractionRules.Add(CreateExtractionRule("ExecutionID"));
//Now the ControlID
session.ExtractionRules.Add(CreateExtractionRule("ControlID"));
}
}
//if we already found the request we needed to add the rule to
//lets look to see if any parameters need to be bound to the value
//from the rule;
if (foundParameters
&& session.FiddlerSession != null)
{
foreach (QueryStringParameter qsp in session.RequestQueryParams)
{
if (qsp.Name.Equals("ExecutionID"))
{
qsp.Value = "{{ExecutionID}}";
}
else if (qsp.Name.Equals("ControlID"))
{
qsp.Value = "{{ControlID}}";
}
}
}
}
#endregion
}
private static ExtractionRule CreateExtractionRule(string Param)
{
//create the rule
ExtractionRule rule = new ExtractionRule(ExtractionRule.ExtractTextClassName);
rule.ContextParameterName = Param;
//add the rule properties
rule.Properties.Add(new RuleProperty(ExtractionRule.StartsWith, Param + "="));
rule.Properties.Add(new RuleProperty(ExtractionRule.EndsWith, "&"));
rule.Properties.Add(new RuleProperty(ExtractionRule.IgnoreCase, "True"));
rule.Properties.Add(new RuleProperty(ExtractionRule.UseRegularExpression, "False"));
rule.Properties.Add(new RuleProperty(ExtractionRule.Required, "True"));
rule.Properties.Add(new RuleProperty(ExtractionRule.Index, "0"));
return rule;
}
}
}
Comments
Anonymous
April 17, 2007
The following article walks you through the process of writing your own fiddler web test plugins andAnonymous
April 18, 2007
Here is another example of a fiddler plugin. This plugin will loop through each request and mark a requestAnonymous
April 18, 2007
Sean Lumley , a developer on the VSTS web test team, has written a series of posts on using a new versionAnonymous
October 02, 2007
I'm can't load the Plugin, no error message. Any suggested way to troubleshoot. I am using v2.1.1.1. Thanks.Anonymous
October 02, 2007
After hours of troubleshooting, discover fiddler also looks for dlls in your Windows user profile folder, like %USERPROFILE%My DocumentsFiddler2ScriptsVSWebTest and this works for me. In my computer, it seems fiddler ignoring any dll dropped in %Program Files%Fiddler2ScriptsVSWebTest.Anonymous
May 14, 2009
Thanks, chrischankit! You saved me hours of troubleshooting :PAnonymous
August 12, 2010
Due to Fiddler's new Import/Export Architecture, this code has changed in Fiddler 2.3. Fiddler 2.3.0.2 (See www.fiddler2.com/.../fiddler2alphasetup.exe until released) re-introduces support for Webtest export plugins. You need to recompile your plugins referencing the new ImportExport VSWebTestExport.dll assembly (which has the WebTest plugin interface). By default, that assembly looks for your plugins in the DocumentsFiddler2ImportExportVSWebTestPlugins folder, unless you've overridden the path using the fiddler.config.path.webtestexport.plugins preference.Anonymous
February 22, 2011
I am tring to use this method to record and auto-correlate web service calls. Fiddler correctly places the body into the The request body is entirely within a String Body within the web test ("StringHttpBodyUI"). How can I gain access to this String Body from within a fiddler Plugin? I don't see a suitable interface within WebTestSession. Thanks.Anonymous
July 10, 2012
Please note: If you want Fiddler v2.x to load your plugin, your project MUST be compiled targeting the .NET Framework v2.0. If you want Fiddler v4.x to load your plugin, you MUST upgrade to the very latest Fiddler v4.x; plugins were broken in v4 until v4.4.0.1.