Friday, June 20, 2008

Getting all the metrics using WebTestRequestPlugin

If there is one shortcoming that I've noticed with VS2008 for load testing it is the default report exporting. With LR (since at least version 6.5 that I can remember) we've always had the ability to export the final output of page response times to an Excel spreadsheet.

At my previous employer that was part of the routine: Analyze results, export to Excel spreadsheet and then use some custom software to crunch the before and after results into a snazzy easy to read format (props to Brian of www.alphasixty.com!).

From what I've seen with VS2008 so far I see that the exported XML format (with an extension of TRX) contains a lot of good data and it is in an XML format but it isn't as simple as opening up an Excel spreadsheet with all the page response times and slicing and dicing beyond that. So, I still have to give props to LR for that.

Something that I've always wanted from LR was to get even more information. More page response times and the response times of the objects that were pulled by the pages that we were testing. Transaction times is all we ever got.

Well, while playing with VS2008 today I figured out some nifty stuff. Microsoft has done a pretty good job of allowing the tester who happens to be a coder to develop code to do just this time of thing that I want and I am doing it with the WebTestRequestPlugin.

Here is the basic class that I wrote to get page response times of the primary call. It's nothing fancy and for right now it just dumps to STDOUT but it easily can (and will be) adapted to insert into a database for futher analysis. I've just dumped the page and the time in milliseconds just for sake of ease to demonstrate.

Here is my plugin code:



   1:      private class GetAllMetrics : WebTestRequestPlugin {

   2:        public override void PostRequest(object sender, PostRequestEventArgs e) {

   3:          string requestedURL = e.Request.UrlWithQueryString.ToString();

   4:          double requestTime  = e.Response.Statistics.MillisecondsToLastByte;

   5:          string outputString = "The resource " + requestedURL + " took " + requestTime.ToString() +

   6:                                " milliseconds to load.";

   7:          Debug.WriteLine(outputString);

   8:        }

   9:      }



Pretty simple, eh? Sure it is. Short and sweet and to the point.

For this example I am hitting AOL.com. The reasons will be clear in a bit.

I made a private instance of this class in my coded web test:



   1:      private GetAllMetrics getMetrics = new GetAllMetrics();



The reason for this will be clear in a moment. Now, wire up the PostRequest to the instance declared above for my request to AOL.com:



   1:        WebTestRequest request1 = new WebTestRequest("http://www.aol.com/");

   2:        request1.ThinkTime = 2;

   3:        request1.PostRequest += new EventHandler<PostRequestEventArgs>(getMetrics.PostRequest);

   4:        yield return request1;

   5:        request1 = null;



When I hit AOL.com I get something like this in my output window:

"The resource http://www.aol.com/ took 272 milliseconds to load."

That is nifty.

After that, I got to thinking. Why can't I wire up all the dependent requests with the same plumbing and get all the metrics for dependent requests?

I couldn't think of any reason why not and coded this method to do so:



   1:      private void setupDependentMetrics(object sender, PostRequestEventArgs e) {

   2:        foreach (WebTestRequest request in e.Request.DependentRequests) {

   3:          request.PostRequest += new EventHandler<PostRequestEventArgs>(getMetrics.PostRequest);

   4:        };

   5:      }



There on line #3 you see where I reference the private instance declared in my coded web test class. Makes sense now, eh?

I wired up a PostRequest with the method that I coded and got this:



   1:        WebTestRequest request1 = new WebTestRequest("http://www.aol.com/");

   2:        request1.ThinkTime = 2;

   3:        request1.PostRequest += new EventHandler<PostRequestEventArgs>(setupDependentMetrics);

   4:        request1.PostRequest += new EventHandler<PostRequestEventArgs>(getMetrics.PostRequest);

   5:        yield return request1;

   6:        request1 = null;



There we go! Let's fire off this bad boy and see what kind of output we get now.



   1:  The resource http://www.aol.com/ took 272 milliseconds to load.

   2:  The resource http://www.aolcdn.com/_media/aolp_v31/main.js took 44 milliseconds to load.

   3:  The resource http://www.aolcdn.com/_media/aolp_v31/main.css took 86 milliseconds to load.

   4:  The resource http://o.aolcdn.com/ads/adsWrapperAT.js took 41 milliseconds to load.

   5:  The resource http://www.aolcdn.com/aolp_mkhome/474f6b9b-00394-029c3-400cb8e1 took 22 milliseconds to load.

   6:  The resource http://o.aolcdn.com/omniunih.js took 51 milliseconds to load.

   7:  The resource http://www.aolcdn.com/aolp_mkhome/474f6b9a-00272-029c3-400cb8e1 took 13 milliseconds to load.

   8:  The resource http://www.aolcdn.com/aolp/oo_engine_main.js took 14 milliseconds to load.

   9:  The resource http://www.aolcdn.com/_media/aolp_v31/updated.gif took 17 milliseconds to load.

  10:  The resource http://www.aolcdn.com/_media/aolp_v31/new.gif took 20 milliseconds to load.

  11:  The resource http://www.aolcdn.com/aolportal/mars-200-061708.jpg took 36 milliseconds to load.

  12:  The resource http://www.aolcdn.com/aolp_dlsnag/snagbutton_default.gif took 12 milliseconds to load.

  13:  The resource http://www.aolcdn.com/aolportal/derek-jeter-yankees-60mh0620.jpg took 20 milliseconds to load.

  14:  The resource http://www.aolcdn.com/_media/aolp_v31/bctrl.gif took 10 milliseconds to load.

  15:  The resource http://www.aolcdn.com/_media/aolp_v31/pctrl.gif took 11 milliseconds to load.

  16:  The resource http://www.aolcdn.com/_media/aolp_v31/fctrl.gif took 11 milliseconds to load.

  17:  The resource http://ar.atwola.com/image/93238809/81806566/aoladp took 114 milliseconds to load.

  18:  The resource http://twx.doubleclick.net/ad/TW.AOLCom/Site_WS_3/Ticker;MN=93238809;wm=o;rdv=xyz;!c=AIR;!c=AMU;!c=APP;!c=ASS;!c=AUC;!c=AUT;!c=AVE;!c=BEV;!c=BSS;!c=BWL;!c=CMP;!c=COS;!c=CPG;!c=DRG;!c=EDU;!c=EYE;!c=FIT;!c=FLE;!c=FOD;!c=GOV;!c=HCS;!c=HOM;!c=HPS;!c=JLY;!c=MED;!c=MFG;!c=MUS;!c=NEW;!c=PET;!c=PHM;!c=PHO;!c=PRI;!c=PUB;!c=RES;!c=RLE;!c=RTL;!c=SPT;!c=TEL;!c=TOB;!c=TOY;!c=TRL;!c=TV;!c=PER;!c=RDO;!c=SRC;!c=TIC;!c=d-fls;!c=d-jav;!c=d-dxp;!c=d-pxp;sz=88x31;dcove=rd;ord=81806566? took 123 milliseconds to load.

  19:  The resource http://www.aolcdn.com/htmlws30_r1/aol_logo_v3.gif took 484 milliseconds to load.

  20:  The resource http://2mdn.aolcdn.com/viewad/1092682/aolmf.gif took 34 milliseconds to load.

  21:  The resource http://www.aolcdn.com/aolmovies/stars-in-hats-denise-richards-78x78 took 27 milliseconds to load.

  22:  The resource http://www.aolcdn.com/aolp_newspromo3/485bd664-00237-00c1e-400cb8e1 took 12 milliseconds to load.

  23:  The resource http://www.aolcdn.com/aolp_newspromo3/485aafec-0019c-06c66-400cb8e1 took 93 milliseconds to load.

  24:  The resource http://www.aolcdn.com/aolportal/couple-holding-hands-120az060508.jpg took 29 milliseconds to load.

  25:  The resource http://www.aolcdn.com/aolp_supertab_mail/4602fec6-002ab-06ee1-0a00ec5a took 11 milliseconds to load.

  26:  The resource http://www.aolcdn.com/_media/aolp_v31/stb_arr_dn took 13 milliseconds to load.

  27:  The resource http://www.aolcdn.com/aolp_w/3s30 took 15 milliseconds to load.

  28:  The resource http://www.aolcdn.com/aolp_supertab_radio/48172fde-00384-00095-400cb8e1 took 15 milliseconds to load.

  29:  The resource http://www.aolcdn.com/aolp_supertab_video/467abd8c-000b9-05971-0a00ead6 took 12 milliseconds to load.

  30:  The resource http://www.aolcdn.com/aolp_page3/45fb58fc-0006d-04433-0a00ec5a took 13 milliseconds to load.

  31:  The resource http://www.aolcdn.com/_media/aolp_v31/stb_arr_up took 11 milliseconds to load.

  32:  The resource http://ar.atwola.com/image/93227127/81806568/aoladp took 52 milliseconds to load.

  33:  The resource http://ar.atwola.com/image/93236004/81806571/aoladp took 116 milliseconds to load.

  34:  The resource http://twx.doubleclick.net/ad/TW.AOLCom/Site_WS_3;MN=93227127;u=re446207ff2c45ddf;wm=o;rdv=xyz;!c=d-jav;sz=300x250;dcove=rd;ord=81806568? took 98 milliseconds to load.

  35:  The resource http://2mdn.aolcdn.com/viewad/1022462/nbcu_hulk_300x250_now.jpg took 36 milliseconds to load.

  36:  The resource http://www.aolcdn.com/pops_promo/fp_ws_0308_re_luxhouse.jpg took 24 milliseconds to load.

  37:  The resource http://twx.doubleclick.net/ad/TW.AOLCom/Site_WS_3;MN=93236004;u=re446207ff2c45ddf;wm=o;rdv=xyz;!c=d-fls;!c=d-jav;!c=d-dxp;!c=d-pxp;sz=121x60;dcove=rd;ord=81806571? took 99 milliseconds to load.

  38:  The resource http://2mdn.aolcdn.com/viewad/817-grey.gif took 10 milliseconds to load.

  39:  The resource http://www.aolcdn.com/pops_promo/wrtflash_v6.js took 12 milliseconds to load.

  40:  The resource http://ar.atwola.com/image/93227135/81806572/aoladp took 51 milliseconds to load.

  41:  The resource http://twx.doubleclick.net/ad/TW.AOLCom/Site_WSA_3;MN=93227135;u=re446207ff2c45ddf;wm=o;rdv=xyz;!c=d-gif;!c=d-jpg;!c=d-imrd;!c=d-fls;!c=d-jav;!c=d-dxp;!c=d-pxp;sz=291x30;dcove=rd;ord=81806572? took 102 milliseconds to load.

  42:  The resource http://2mdn.aolcdn.com/viewad/817-grey.gif took 10 milliseconds to load.



Look at all that! Why did I use aol.com? Because I knew a bunch of crap would be loaded with the home page hit. :^)

I dunno about you but I thought this was pretty nifty.

If figure that if I load the current transaction into the Context of the current request I figure that I can log the transaction and all the associated hits related to those transactions and be able to do some neat number crunching. That'll come later but that's enough for right now.

Here is the entire coded web test:



   1:  namespace WebTestRequestMetricCollection {

   2:    using System;

   3:    using System.Collections.Generic;

   4:    using System.Text;

   5:    using Microsoft.VisualStudio.TestTools.WebTesting;

   6:    using Microsoft.VisualStudio.TestTools.WebTesting.Rules;

   7:    using System.Diagnostics;

   8:    public class AOLHomePage : WebTest {

   9:      private GetAllMetrics getMetrics = new GetAllMetrics();

  10:      public AOLHomePage() {

  11:        this.PreAuthenticate = true;

  12:      }

  13:      public override IEnumerator<WebTestRequest> GetRequestEnumerator() {

  14:        // Initialize validation rules that apply to all requests in the WebTest

  15:        if ((this.Context.ValidationLevel >= Microsoft.VisualStudio.TestTools.WebTesting.ValidationLevel.Low)) {

  16:          ValidateResponseUrl validationRule1 = new ValidateResponseUrl();

  17:          this.ValidateResponse += new EventHandler<ValidationEventArgs>(validationRule1.Validate);

  18:        }

  19:        WebTestRequest request1 = new WebTestRequest("http://www.aol.com/");

  20:        request1.ThinkTime = 2;

  21:        request1.PostRequest += new EventHandler<PostRequestEventArgs>(setupDependentMetrics);

  22:        request1.PostRequest += new EventHandler<PostRequestEventArgs>(getMetrics.PostRequest);

  23:        yield return request1;

  24:        request1 = null;

  25:      }

  26:      private void setupDependentMetrics(object sender, PostRequestEventArgs e) {

  27:        foreach (WebTestRequest request in e.Request.DependentRequests) {

  28:          request.PostRequest += new EventHandler<PostRequestEventArgs>(getMetrics.PostRequest);

  29:        };

  30:      }

  31:      private class GetAllMetrics : WebTestRequestPlugin {

  32:        public override void PostRequest(object sender, PostRequestEventArgs e) {

  33:          string requestedURL = e.Request.UrlWithQueryString.ToString();

  34:          double requestTime  = e.Response.Statistics.MillisecondsToLastByte;

  35:          string outputString = "The resource " + requestedURL + " took " + requestTime.ToString() +

  36:                                " milliseconds to load.";

  37:          Debug.WriteLine(outputString);

  38:        }

  39:      }

  40:    }

  41:  }

No comments: