Showing posts with label Correlation. Show all posts
Showing posts with label Correlation. Show all posts

Thursday, August 7, 2008

Coding like Ike and Tina...

I'm working on a WebTestRequest that changes an address and in the post back the POST data contains a bunch of information that is displayed in the HTML and I need to extract data from text boxes, check boxes and drop down lists. Extracting the textboxes and checkboxes are pretty simple, it is the drop down boxes that are a pain.

The .NET HTML parser treats the HtmlTags that reside inside the SELECT tag separately so I cannot just get the attributes for the select and then slice and dice and get the default selected value for the drop in.

I tried to be "elegant" in my solution and use the HTML Agility Pack and a XPath pulled from FireBug but alas, there was no love to be found. I'm not sure what the exact problem is other than me being a total XPath n00b (I normally use an open source Java project to extract XPaths from XML docs) but something was amiss. After wasting several hours trying to get the elegant solution I decided to be like Ike and Tina and just brute force the solution of identifying the default selected value for the drop down lists. I populate the Context of the WebTestRequest with all the values that are extracted and just reference them by element name in the address change POST ala the HIDDEN1$ that VS2008 does for hidden field extraction and it seems to work pretty good.

Here is the extraction code that I wrote to pull the fields that I needed to reference in the Context:



   1:  void ExtractCustomerDataToContext(object sender, ExtractionEventArgs e) {

   2:    Dictionary<string, string> customerData = new Dictionary<string, string>();

   3:    string lastDropDown = null;

   4:    foreach (HtmlTag tag in e.Response.HtmlDocument.HtmlTags) {

   5:      string type = tag.GetAttributeValueAsString("type");

   6:      if (type != null) {

   7:        lastDropDown = null ;

   8:        if (type == "text" || type == "checkbox") {

   9:          if (tag.GetAttributeValueAsString("checked") != null) {

  10:            if (tag.GetAttributeValueAsString("checked") == "checked") {

  11:              customerData.Add(tag.GetAttributeValueAsString("name"), "on");

  12:            } else {

  13:              customerData.Add(tag.GetAttributeValueAsString("name"), "off");

  14:            };

  15:          } else {

  16:            customerData.Add(tag.GetAttributeValueAsString("name"), tag.GetAttributeValueAsString("value"));

  17:          };

  18:        };

  19:      } else {

  20:        if (tag.GetAttributeValueAsString("class") != null) {

  21:          if (tag.GetAttributeValueAsString("class") == "dropDownListDefault") {

  22:            lastDropDown = tag.GetAttributeValueAsString("name");

  23:          };

  24:        } else {

  25:          if (tag.GetAttributeValueAsString("selected") != null) {

  26:            if (lastDropDown != null) {

  27:              customerData.Add(lastDropDown, tag.GetAttributeValueAsString("value"));

  28:            };

  29:          };

  30:        };

  31:      };

  32:    };

  33:    foreach (KeyValuePair<string, string> kvp in customerData) {

  34:      if (this.Context.ContainsKey("CUSTOMER_UPDATE_DATA." + kvp.Key)) {

  35:        this.Context.Remove("CUSTOMER_UPDATE_DATA." + kvp.Key);

  36:      };

  37:      if (kvp.Value == null) {

  38:        this.Context.Add("CUSTOMER_UPDATE_DATA." + kvp.Key, "");

  39:      } else {

  40:        this.Context.Add("CUSTOMER_UPDATE_DATA." + kvp.Key, kvp.Value);

  41:      };

  42:    };

  43:  }



So, if the previous page HTML has a text field by the name of "txtCustomerAccountNumber" I can simply add that information to the post with:



   1:  changeAddressRequestBody.FormPostParameters.Add("txtCustomerAccountNumber", this.Context["CUSTOMER_UPDATE_DATA.txtCustomerAccountNumber"].ToString());



As long as the information was displayed via HTML in the prior hit, I should be good to go.

Wednesday, August 6, 2008

Revisiting CSV Data Binding in VS2008 Coded Web Tests

I've been going through the WinDBG labs located here. Tess has done a great job with the labs and done all the hard work, so w00t for her!

I needed to add another .CSV data file for my main VUser that I am currently working on so I did a copypasta of a pre-existing DataBinding and DataSource. When I first created the coded web test I had added so much custom code that I deleted the web test and left only the coded web test. The fear was that I might accidently re-gen the code from the web test and over write my work. That in and of itself shouldn't be too bad since we all know to use a code repository, right? Of course we do.

Anyhoo, I made the code changes and was greeted by an error when my file couldn't be loaded. Huh? How rude!

I finally got it to work, but I wanted to note the way .CSV data binding works in a coded web test in greater detail just to remind myself next time this happens. There are plenty of examples of binding a .CSV file to a web test, but few on a pre-existing coded web test. So, here goes!

First we have to have a DataSource declaration that takes five parameters for a .CSV file:

1: dataSourceName
This is the name we are giving the DSN for our .CSV file.
2: providerName
In my case for a .CSV file I am using "Microsoft.VisualStudio.TestTools.DataSource.CSV"
3: connectionString.
For my use, this is the path with escaped back-whacks to the .CSV file. In my case, it is "c:\\data\\streetNames.csv"
4: DataBindingAccessMethod
For my use, I want a random selection of data from the .CSV file so I am using "Microsoft.VisualStudio.TestTools.WebTesting.DataBindingAccessMethod.Random"
5: tableName
I thought that I could provide my own table name to reference the .CSV file but that didn't work and was forced to use the form of #csv, like this: "streetNames#csv". When I tried to use the name "streetNameTable" I would get back an error of "cannot find streetTableName.txt" from VS2008. But, the #csv form works, so no biggie.

Here is what I am using for my entire Data Source:



   1:    [DataSource("streetNameDataSource",

   2:              "Microsoft.VisualStudio.TestTools.DataSource.CSV",

   3:              "C:\\Data\\streetNames.csv",

   4:              Microsoft.VisualStudio.TestTools.WebTesting.DataBindingAccessMethod.Random,

   5:              "streetNames#csv")]



Next, we have to define what column we are pulling from the .CSV file. In my case, it is a single column CSV file but we still have to have a column name defined. For my CSV file I am using the form of:

8<---------------------------
streetNames
"ABBINGTON"
"ALDRICH"
"ALEXANDER"
"ALICE"
"ALLISON"
"ALMOND"
"ALTA"
"AMHERST"
--------------------------->8

These are all the street names in Grover's Mill, NJ as reported by Melissa Data.

The DataBinding declaration that I am using takes four parameters.

1: dataSourceName
This is the DSN that we previously defined.
2: tableName
This is the tableName that we defined in the form of #csv.
3: columnName
This is the column name of the CSV that we are extracting. I've found this to be case insensitive.
4: contextVariableName
This is the key name of the entry that will be created in the Context for the iteration.

Here is what I am using for my DataBinding:



   1:    [DataBinding("streetNameDataSource", "streetNames#csv", "streetNames", "streetNames")]



Now with each iteration of the vuser I can reference the Context for the randomized street name, ala:



   1:  Debug.WriteLine("Street name is " + this.Context["streetNames"].ToString());



Works like a champ!

Wednesday, June 18, 2008

Loading parameter data from a CSV in a VS2008 coded web test

Today I've been working on how to load correlated data from a CSV file into a coded web test. VS2008 has support for a wide range of data sources for correlation but I still prefer the good 'ol CSV format.

I rag on LR a lot, but selecting a data file and accessing it with the "{pSomeParam}" format is much easier in LR than VS2008. Details below:

In my little webtest I recorded a simple script hitting Google and I wanted to be able to pass different search terms to Google. Nothing fancy, just enough to figure out how to do what I wanted. This task in LR is pretty trivial.

I created a file by the name of searchTerms.csv that contains the following:

8<------------------
column1
loadrunner
perl
superbase
vs2008
------------------>8

I then added a data source to the web test and converted it over to code to see how I need to code things in the future and this is how it looks:



   1:  [DataSource("searchTerms", 

   2:              "Microsoft.VisualStudio.TestTools.DataSource.CSV", 

   3:              "|DataDirectory|\\databindingtowebtest\\searchTerms.csv", 

   4:              Microsoft.VisualStudio.TestTools.WebTesting.DataBindingAccessMethod.Sequential,

   5:              "searchTerms#csv")]

   6:  [DataBinding("searchTerms", "searchTerms#csv", "column1", "GoogleSearchTerm")]



The context for the current web test should now be automagically updated with an index entry by the name of "GoogleSearchTerm" that is referenced like this:



   1:  request2.QueryStringParameters.Add("q", this.Context["GoogleSearchTerm"].ToString(), false, false);



I don't mind the accessing of the correlated data via the context. That is no big deal, but setting up the databinding is kind of a complicated PITA. I have to give nods to LR at this point for the ease of creating params. But, on the other hand, VS2008 gives me a lot more control of slicing and dicing of return HTML data so there had to be a trade off someplace and I guess this is one of those situations.

Something important that I've learned is that numeric data needs to be double quoted to prevent mangling of the values. For example, I have the following entries now:



  [DataSource("lastNames", 

              "Microsoft.VisualStudio.TestTools.DataSource.CSV", 

              "c:\\work\\data\\LRData\\lastNames.csv", 

              Microsoft.VisualStudio.TestTools.WebTesting.DataBindingAccessMethod.Random, //   .Sequential, 

              "lastNames#csv")]

 

  [DataSource("targetServer",

            "Microsoft.VisualStudio.TestTools.DataSource.CSV",

            "c:\\work\\data\\LRData\\targetServer.csv",

            Microsoft.VisualStudio.TestTools.WebTesting.DataBindingAccessMethod.Sequential,

            "targetServer#csv")]

 

  [DataBinding("lastNames", "lastNames#csv", "lastName", "lastName")]

  [DataBinding("targetServer", "targetServer#csv", "TargetServer", "targetServer")]



The IP address that I want to hit is bound to the context entry for "targetServer."

I found that if I have the entry as:

8<----------------------
TargetServer
10.0.0.100
---------------------->8

I find that if it is not quoted it will be manged into the value "10.001." Just adding the double quotes around the value will allow me to use the IP address as I originally intended.



string targetWebServer = this.Context["targetServer"].ToString();

WebTestRequest request1 = new WebTestRequest("http://" + targetWebServer.ToString() + "/someVDir/somePage.aspx");



Knowing is half the battle. (GI Joe!)