Second Life of a Hungarian SharePoint Geek

September 21, 2012

Performance testing SharePoint applications

Filed under: SharePoint, SP 2010, Testing — Tags: , , — Peter Holpar @ 19:34

Note (to avoid disappointment): This post is not intended to be a general introduction or walkthrough of performance testing of web / SharePoint applications. It is rather just a quick tip on a tool I found useful performing such tasks.

A developer should never forget, that functional testing is only one – although a very important one – part of the application testing. There are other things to test, for example, how do the components of your system behave in a stress situation.

Recently we had to perform a load test on a server-intensive Silverlight application. The client side calls a lot of RIA web services methods to query and update data stored in the SQL database and other back-end systems. So I had to choose a tool that makes our test easy to perform.

In the past we already had a similar issue with a SharePoint application, where we decided to use Web Performance Test Recorder included in Visual Studio Ultimate for the load test, but unfortunately in this case the Ultimate edition was no option due to licensing considerations.

My next idea was Fiddler, the free tool I used quite frequently to monitor network traffic (especially when playing with the SharePoint Client Object Model), and whose ability to record and perform such tests I was beware of, but before starting the actual test I wanted to check the SL forums to see the experience of other developers in this area. In this thread I found a particularly interesting Fiddler extension called StresStimulus, as well as a quick start about how to use this tool.

I’ve downloaded a trial version of this product, and found it can really help the web developer (on a relative low budget) to monitor the performance of the network and the server on a high load situation.

A few of the nice features of the tool are the ability to parameterize the requests sent to the server as well as the capability to use a set of username/password pairs for authentication.

// Regarding SharePoint, the TechNet article about performance testing describes the Visual Studio Team System (Team Test Load Agent), and MSDN shows the usage of the Web Performance Test Recorder, but the list of performance counters to monitor is independent from the tool you choose for testing.

July 15, 2012

Emulating slow server response times using Fiddler

Filed under: Fiddler, Testing — Tags: , — Peter Holpar @ 20:22

Recently I’m working on a Silverlight business application that communicates with the server side using RIA web services. On the customer’s test system we experienced an unpleasant behavior in the asynchronous operations due to the slow response time of some background systems. In our development system we were not able to reproduce these issues.

Fortunately, Fiddler can help to emulate the slow server response times as well as slow network speeds.

Setting slow network speed is easy using the UI. One should check the Simulate Modem speeds option in Rules / Performance.

image 

If you need to set custom speeds, you should modify the upload / download speeds (delay ms per KB) through the request-trickle-delay and response-trickle-delay session parameters (see these samples for details) in the CustomRules.js (Rules / Customize Rules…).

Our case was a bit complicated, as the amount of the network traffic was really not proportional with the response times. Small response packages was just as slow as huge ones. So I had to use a custom, fix delay in the OnBeforeResponse function.

I have achieved this delay using a custom wait function:

static function wait(msecs)
{
var start = new Date().getTime();
var cur = start;
while(cur – start < msecs)
{
   cur = new Date().getTime();
}
}

Then I can use this call in OnBeforeResponse:

wait(5000);

However, using this simple function call would make every response slow, not only the ones received from our test web server. For example, a web search, or checking your web mail on the same system would be slow as well – a quite inconvenient side effect.

The solution to the problem was an extra condition:

if (oSession.HostnameIs("localhost:64399"))
{
  wait(5000);
}

where localhost:64399 is the name and the port number of our test web system.

August 9, 2010

Using Explorer add-ins as web application “test engines”

Filed under: IE add-in, Testing — Tags: , — Peter Holpar @ 20:07

If you develop web applications day-by-day and that applications require a lot of user inputs in terms of typing text in text boxes, selecting values from lists and  check boxes, you probably know how frustrating it is for a developer to type and click so much each time just to test his code and  how useful it would be to have a tool that makes it automatically.

// Note: You can say that there are such tools, and you are right, but these tools do not always do exactly what you need and do cost usually a lot of bucks, as they can do lot more than you actually need.

I felt the same when I worked on a project with rather complex user interfaces. To test functionality that is integrated in the UI I had to fill out dozens of text boxes, select from another dozen check box and list, repeating these steps several times in each hour, typically with the same sets of input data.

After a few days I got an idea of creating a simple IE add-in to help me in this tedious work. Although I was moved to more exciting projects in the meantime, where I can get more on my programming skills than typing speed, I’ve created a sample add-in driven just by curiosity.

Again, I chose Add-in Express 2008 for Internet Explorer to implement this sample (you can find the latest version here).

Besides the add-in project I’ve created a simplified web application project for a user interface that handles fictitious contracts. I stored contract test data in XML files. This data is used to populate contract field on UI, but more on that later.

Of course, in the real world you would have more complicated user interfaces and more contract types, but I hope I can illustrate the main points easier this way.

You can get both the add-in and contracts web projects (including some sample contract XML files) in Visual Studio 2008 format here in a single file download. The solution was tested on IE 7 and 8.

Before the internal details let’s see the add-in in action. After the add-in is installed, one can display it by selecting the WebApp AutoFill menu from the Explorer Bar menu of IE, as shown below (IE 7 screen):

image

The folder location of the contract data XML is stored in the registry (HKEY_CURRENT_USER\Software\Company\TestBar key,  ContractFolderPath value), but you can set that from the UI as well by clicking on the ellipsis (…).

image

In this case I used the XML files provided with the web project in the sample (TestContracts\ContractSamples), as reflected on the add-in UI.

image

When you start the web application project, a private contract form is displayed by default.

image

The add-in UI adapts to the content of the browser, and displays the test contact XML files that stores private contract data.

image

When you select one of the test data files, the input fields are populated based on the data contained in the file.

image

When you change to the business contracts using the link in the menu form, a business contract is displayed.

image

At the same time the add-in UI is updated with the business contract test data XML files.

image

Again, selecting one of those test files, the input fields are populated.

image

Now let’s see, how I mapped contract type and contract data between the UI and XML data files.

As you can see in the source of the ASPX page, I’ve used a hidden field called CustomerType and note the values of the ID property of the web control, that will be translated to the HTML name property when the page is rendered.

image

In the XML sample below you can see the corresponding values:

image

Now it is time to see some lines of code.

When a new page is loaded, I refresh the add-in UI. First I try to get the right frame (GetFrame method) and the customer type from the hidden field (GetValueOfHiddenField method) then update the file list (UpdateContractFileList method) accordingly.

  1. private void TestBar_DocumentComplete(object pDisp, string url)
  2.  {
  3.      RefreshInfo();
  4.  }
  5.  
  6.  private void RefreshInfo()
  7.  {
  8.      CustomerType customerType = CustomerType.Unknown;
  9.      try
  10.      {
  11.          if (IEApp != null)
  12.          {
  13.              if (Visible)
  14.              {
  15.  
  16.                  HTMLDocument doc = HTMLDocument;
  17.  
  18.                  if (doc != null)
  19.                  {
  20.                      IHTMLWindow2 mainFrame = BrowserHelper.GetFrame(doc, "CONTRACT");
  21.  
  22.                      if (mainFrame != null)
  23.                      {
  24.                          IHTMLDocument2 mainFrameDoc = mainFrame.document;
  25.  
  26.                          String customerTypeString = BrowserHelper.GetValueOfHiddenField(mainFrameDoc, "CustomerType");
  27.  
  28.                          customerType = Utils.ParseEnum<CustomerType>(customerTypeString);
  29.                      }
  30.                  }
  31.              }
  32.          }
  33.      }
  34.      catch (UnauthorizedAccessException ex)
  35.      {
  36.          IEApp.StatusText = String.Format("{0} – The page may contain frames from different domains.", ex.Message);
  37.      }
  38.      catch (Exception ex)
  39.      {
  40.          MessageBox.Show(ex.Message);
  41.      }
  42.  
  43.      // Update current values and diplay in case of change
  44.  
  45.      CustomerType formerCustomerType = CustomerType;
  46.      if ((customerType != CustomerType.Unknown))
  47.      {
  48.          CustomerType = customerType;
  49.          customerTypeLabel.Text = CustomerType.ToString();
  50.      }
  51.      else
  52.      {
  53.          CustomerType = CustomerType.Unknown;
  54.          customerTypeLabel.Text = NA_TEXT;
  55.      }
  56.      // we update only in case of change
  57.      if ((formerCustomerType != CustomerType))
  58.      {
  59.          UpdateContractFileList();
  60.      }
  61.  }

Note, that I’ve found that there might be an UnauthorizedAccessException exception raised  in the add-in when the frames are from different domains. That behavior might have been changed in the latest releases of the product. I show the exception in this case in the Status Bar of the browser.

It is the code of the GetFrame and GetValueOfHiddenField methods:

  1. public static IHTMLWindow2 GetFrame(HTMLDocument document, String frameName)
  2.         {
  3.             IHTMLWindow2 result = null;
  4.             if (document != null)
  5.             {
  6.                 FramesCollection frames = (FramesCollection)document.frames;
  7.                 for (int i = 0; i < frames.length; i++)
  8.                 {
  9.                     object refIndex = i;
  10.                     IHTMLWindow2 frame = (IHTMLWindow2)frames.item(ref refIndex);
  11.                     if (frame.name == frameName)
  12.                     {
  13.                         result = frame;
  14.                         break;
  15.                     }
  16.                 }
  17.             }
  18.             return result;
  19.         }
  20.  
  21.         public static String GetValueOfHiddenField(object document, String name)
  22.         {
  23.             String result = null;
  24.             IHTMLDocument3 htmlDoc = (IHTMLDocument3)document;
  25.             IHTMLElementCollection elements = htmlDoc.getElementsByName(name);
  26.             for (int i = 0; i < elements.length; i++)
  27.             {
  28.                 object refIndex = i;
  29.                 IHTMLElement element = (IHTMLElement)elements.item(name, refIndex);
  30.                 try
  31.                 {
  32.                     if ((element.tagName.ToUpper() == "INPUT") && (((String)(element.getAttribute("TYPE", 0))).ToUpper() == "HIDDEN"))
  33.                     {
  34.                         result = ((String)(element.getAttribute("VALUE", 0)));
  35.                         break;
  36.                     }
  37.                 }
  38.                 catch (Exception ex)
  39.                 {
  40.                     MessageBox.Show(ex.Message);
  41.                 }
  42.             }
  43.             return result;
  44.         }

The code of the UpdateContractFileList method is shown here:

  1. private void UpdateContractFileList()
  2. {
  3.     fileList.Items.Clear();
  4.     if ((!String.IsNullOrEmpty(_contractFolderPath)) && (Directory.Exists(_contractFolderPath))
  5.       && (CustomerType != CustomerType.Unknown))
  6.     {
  7.         foreach (String file in Directory.GetFiles(_contractFolderPath, "*.xml"))
  8.         {
  9.             if (IsTypeMatch(file, CustomerType))
  10.             {
  11.                 fileList.Items.Add(GetFileName(file));
  12.             }
  13.         }
  14.     }
  15. }

When the user select a new file from the list, I try to populate the input fields one-by-one based on the current customer type and the test data.

  1. private void fileList_SelectedIndexChanged(object sender, EventArgs e)
  2.  {
  3.      object fileListItem = fileList.SelectedItem;
  4.      if (fileListItem != null)
  5.      {
  6.          HTMLDocument doc = HTMLDocument;
  7.          IHTMLWindow2 mainFrame = BrowserHelper.GetFrame(doc, "CONTRACT");
  8.          if (mainFrame != null)
  9.          {
  10.              IHTMLDocument2 mainFrameDoc = mainFrame.document;
  11.              try
  12.              {
  13.                  XmlDocument contractXml = new XmlDocument();
  14.                  contractXml.Load(_contractFolderPath + "\\" + (String)fileListItem);
  15.  
  16.                  if (_customerType == CustomerType.Private)
  17.                  {
  18.                      BrowserHelper.FillTextBox(mainFrameDoc, contractXml, "FirstName");
  19.                      BrowserHelper.FillTextBox(mainFrameDoc, contractXml, "LastName");
  20.                      BrowserHelper.FillTextBox(mainFrameDoc, contractXml, "City");
  21.                      BrowserHelper.FillTextBox(mainFrameDoc, contractXml, "Street");
  22.  
  23.                      BrowserHelper.SelectRadioButton(mainFrameDoc, contractXml, "Gender");
  24.                  }
  25.                  else if (_customerType == CustomerType.Business)
  26.                  {
  27.                      BrowserHelper.FillTextBox(mainFrameDoc, contractXml, "CompanyName");
  28.                      BrowserHelper.FillTextBox(mainFrameDoc, contractXml, "TaxNumber");
  29.                      BrowserHelper.FillTextBox(mainFrameDoc, contractXml, "City");
  30.                      BrowserHelper.FillTextBox(mainFrameDoc, contractXml, "Street");
  31.  
  32.                      BrowserHelper.SelectRadioButton(mainFrameDoc, contractXml, "EmployeeCount");
  33.                  }
  34.                  
  35.  
  36.              }
  37.              catch (Exception ex)
  38.              {
  39.                  MessageBox.Show(ex.Message);
  40.              }
  41.          }
  42.      }
  43.  
  44.  }

It is the code of the SelectRadioButton and the FillTextBox methods used to update the fields:

  1. public static void SelectRadioButton(object document, XmlDocument contractXml, String fieldName)
  2. {
  3.     try
  4.     {
  5.         IHTMLDocument3 htmlDoc = (IHTMLDocument3)document;
  6.         IHTMLElementCollection elements = htmlDoc.getElementsByName(fieldName);
  7.  
  8.         String fieldValue = contractXml.SelectSingleNode(String.Format("//*[@name='{0}']", fieldName)).InnerText;
  9.  
  10.         for (int i = 0; i < elements.length; i++)
  11.         {
  12.             object refIndex = i;
  13.             IHTMLElement element = (IHTMLElement)elements.item(fieldName, refIndex);
  14.  
  15.             if ((element.tagName.ToUpper() == "INPUT") && (((String)(element.getAttribute("TYPE", 0))).ToUpper() == "RADIO"))
  16.             {
  17.                 if (((String)(element.getAttribute("VALUE", 0))).ToUpper() == fieldValue.ToUpper())
  18.                 {
  19.                     element.click();
  20.                     break;
  21.                 }
  22.             }
  23.         }
  24.     }
  25.     catch (Exception ex)
  26.     {
  27.         MessageBox.Show(ex.Message);
  28.     }
  29. }
  30.  
  31. public static void FillTextBox(object document, XmlDocument contractXml, String fieldName)
  32. {
  33.     try
  34.     {
  35.         IHTMLDocument3 htmlDoc = (IHTMLDocument3)document;
  36.         IHTMLElementCollection elements = htmlDoc.getElementsByName(fieldName);
  37.  
  38.         String fieldValue = contractXml.SelectSingleNode(String.Format("//*[@name='{0}']", fieldName)).InnerText;
  39.  
  40.         if (elements.length > 0)
  41.         {
  42.             object refIndex = 0;
  43.             IHTMLElement element = (IHTMLElement)elements.item(fieldName, 0);
  44.  
  45.             element.setAttribute("VALUE", fieldValue, 0);
  46.         }
  47.     }
  48.  
  49.     catch (Exception ex)
  50.     {
  51.         MessageBox.Show(ex.Message);
  52.     }
  53. }

You should add new methods to handle other types of input beyond text and check box.

I hope the sample above illustrates well how practical it can be sometimes to use an IE add-in to fill out web application test data. It helps you to spend more time on issues that require more creativity than typing.

Note: The add-in solution contains a post-build step that deploys the built assembly into the default installation location. Of course, it runs correctly only after you have installed the add-in and you left the default location on installation. You might want to alter or remove that step depending on your needs.

Theme: Shocking Blue Green. Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.

Join 42 other followers