Monthly Archives: May 2016

a loadtest plugin to generate a GUID per request

Visual Studio has an out-of-the box plugin that generates GUIDs for you. However its a WebTest plugin, so it will only generate them once per webtest. You can’t include that plugin into a loop.

Fortunately we can write a simple WebTestRequest plugin that does what we want:

using System;
using System.ComponentModel;
using Microsoft.VisualStudio.TestTools.WebTesting;
using Microsoft.VisualStudio.TestTools.WebTesting.Rules;

namespace LoadTestPlugins
{
    [DisplayNameAttribute("Generate GUID")]
    [DescriptionAttribute("Generates a GUID during a request")]
    public class GenerateGUID : WebTestRequestPlugin
    {

        #region Methods
        private void GenerateIt(WebTestContext Context)
        {
            Context[this.ContextParameterName] = Guid.NewGuid().ToString();
        }

        public override void PostRequest(object sender, PostRequestEventArgs e)
        {
            if(this.BeforeRequest == false)
            {
                this.GenerateIt(e.WebTest.Context);
            }
        }

        public override void PreRequest(object sender, PreRequestEventArgs e)
        {
            if(this.BeforeRequest == true)
            {
                this.GenerateIt(e.WebTest.Context);
            }
        }
        #endregion

        #region Properties
        [Description("Name of context parameter to store the number in"),
        DisplayName("Context parameter"), IsContextParameterName(true)]
        public string ContextParameterName {get; set;}

        [Description("Whether to generate the number before or after the request has executed"),
        DisplayName("Apply before request"),
        DefaultValue(true)]
        public bool BeforeRequest { get; set; }
        #endregion
    }
}

A loadtest plugin to clear context parameters (and why we need it)

If a webtest stores temporary data in a context parameter, then you need to make sure that its reset to some initial state when the webtest starts. Otherwise it will fail if that webtest is included multiple times within the same testrun.

Visual Studio’s “Set Context Parameter Value” plug-in won’t help here as it cannot set the value of a parameter to an empty string. If you try this, then during the test run, the request will fail with the error “Request failed: Missing or invalid Context Parameter Name.”

The solution in my case was a very simple custom plug-in. I choose to implement it as a WebTestPlugin instead of a WebTestRequestPlugin to avoid confusing it with Visual Studio’s existing plugin.

using System.ComponentModel;
using Microsoft.VisualStudio.TestTools.WebTesting;
using Microsoft.VisualStudio.TestTools.WebTesting.Rules;

namespace LoadTestPlugins.WebTestPlugins
{
    [DisplayNameAttribute("Set context parameter value")]
    [DescriptionAttribute("Sets the context parameter to a value")]
    public class ClearContextParameter : WebTestPlugin
    {
        public override void PreWebTest(object sender, PreWebTestEventArgs e)
        {
            e.WebTest.Context[this.ContextParameter] = this.Value;
        }

        #region Properties
        [Description("Context parameter to set"),
        DisplayName("Context parameter"), IsContextParameterName(true)]
        public string ContextParameter { get; set; }

        [Description("Value to place into the context parameter"),
        DisplayName("Value")]
        public string Value { get; set; }
        #endregion
    }
}

SharePoint people-pickers in loadtests

A significant number of the webtests in out loadtest need to simulate a human tying into a people-picker control. This control waits until the first few letters have been entered and after that calls SharePoint’s client.svc web-service every time the user enters data into the control.

I simulate this with the following logic in webtests:

GET https://...
For Each Character In Targetusers display name:
    POST https://.../_vti_bin/client.svc/ProcessQuery
GET https:/...

As Visual Studio doesn’t contain a plugin that iterates over characters in a string, I wrote my own. This plug-in allows you to choose:

  1. Which input parameter contains the display name to iterate over.
  2. Into which output parameter to place the sub-string.
  3. At which index to start iterating. The 1st iteration of the loop includes all characters upto the starting index. All subsequent iterations add one more letter from the input string into the output string.

Some nice-to-have improvements to this plugin are:

  • Add a configurable delay in milliseconds to each loop. This would be nice as think-times on requests can only be configured in intervals of whole seconds
  • Add an option to automatically clear the output parameter once all iterations have finished

Here is the code for the plug-in:

using System.Collections.Generic;
using System.ComponentModel;
using Microsoft.VisualStudio.TestTools.WebTesting;
using Microsoft.VisualStudio.TestTools.WebTesting.Rules;

namespace LoadTestPlugins
{

    [DisplayNameAttribute("For Each character in context parameter")]
    [DescriptionAttribute("Sequentially copies each character from the source into the destination. You can use this to simulate a user that types a name into a peoplepicker for example")]
    public class ForEachCharacter : ConditionalRule
    {

        #region Methods
        public override void CheckCondition(object sender, ConditionalEventArgs e)
        {
            string Input = e.WebTest.Context[this.InputContextParameterName].ToString();
            string Output;
            try
            {
                Output = e.WebTest.Context[this.OutputContextParameterName].ToString();
            }
            catch (KeyNotFoundException)
            {
                Output = string.Empty;
            }


            //Only do something if the output is not yet complete
            if (Input.Length == Output.Length)
            {
                e.IsMet = false;
                return;
            }
            

            int LengthToCopy;
            if(Output.Length == 0)
            {
                //Copy all characters before the starting index and 1 extra
                LengthToCopy = this.StartIndex + 1;
            }
            else 
            {
                //Copy 1 extra character to the output
                LengthToCopy = Output.Length + 1;
            }

            e.IsMet = true;
            e.WebTest.Context[this.OutputContextParameterName] = Input.Substring(0, LengthToCopy);
            return;
        }
        
        #endregion

        #region Properties
        [Description("Name of context parameter that contains the characters to iterate over"),
        DisplayName("Input Context parameter"), IsContextParameterName(true)]
        public string InputContextParameterName {get; set;}

        [Description("Name of context parameter to store the each successive iteration"),
        DisplayName("Ouput Context parameter"), IsContextParameterName(true)]
        public string OutputContextParameterName { get; set; }

        [Description("The zero-based index to start the iteration from"),
        DisplayName("Start Index")]
        public int StartIndex { get; set; }
        #endregion
    }
}