Load Test Modeling: How to Simulate User Logon and Execute Tests in a Test Mix



The following sample shows how to model the common scenario where a user logs on once to a Web site and performs a number of tests.  


 


In the sample, the logon action is definded by a set of login requests. Each Web test contains the same set of login requests plus its own non-login requests.


 


A “new user” as defined by “Percentage of New Users” under the Load test scenario property always executes Login once and one iteration of a Web test (the non-login section)  from the Test Mix. A “return user” always executes Logon once and a random number of Web tests (the non-login sections) from the Test Mix.


 


Modeling can be achieved by using plug-ins or coded web tests .


==============================================================================


A. To do this using plug-ins:



  1. Create a new test project.

  2. Add WebRequestPlugin.cs, WebTestPlugin.cs, LoadTestPlugin.cs, VUserInfo.cs and WebTestUserMonitor.cs to the project.

  3. Create the web tests. Each Web test contains the same set of login request(s). i.e

            WebTest1


                        login requests


                        non-login requests


 


            WebTest2


                        login requests


                        non-login requests


 


            WebTest3


                        login requests


                        non-login requests


 


            Suffix the set of login requests. i.e,


 


If the original set of login requests are


 


http://server/loginRequest1.aspx


http://server/loginRequest2.aspx


http://server/loginRequest3.aspx



http://server/loginRequestN.aspx



 


Login requests after appending suffixes will be


 


http://server/loginRequest1.aspxLoginRequestFirst


http://server/loginRequest2.aspxLoginRequest


http://server/loginRequest3.aspxLoginRequest



http://server/loginRequestN.aspxLoginRequestLast



 



  1. Add the Web test and request plug-ins to all of your Web tests

  2. Add the web tests to a load test scenario

  3. Optional. Add the load test plug-in to the load test if you want to output the number of login test executed. You can generate your own report by modifying the Stop() in WebTestUserMonitor class.

 


WebTestPlugin.cs


 


using System;


using System.Collections.Generic;


using System.Text;


using Microsoft.VisualStudio.TestTools.WebTesting;


 


namespace TestMixPlugins


{


    public class TestPlugin : WebTestPlugin


    {


        public override void PreWebTest(object sender, PreWebTestEventArgs e)


        {


            WebTestUserMonitor.Instance.PreWebTest(sender, e);


        }


 


        public override void PostWebTest(object sender, PostWebTestEventArgs e)


        {


            WebTestUserMonitor.Instance.PostWebTest(sender, e);


        }


    }


}


 


WebRequestPlugin.cs


 


using System;


using System.Collections.Generic;


using System.Text;


using Microsoft.VisualStudio.TestTools.WebTesting;


 


namespace TestMixPlugins


{


    public class RequestPlugin : WebTestRequestPlugin


    {


        public override void PreRequest(object sender, PreRequestEventArgs e)


        {


            WebTestUserMonitor.Instance.login_PreRequest(sender, e);


        }


 


        public override void PostRequest(object sender, PostRequestEventArgs e)


        {


            bool login = e.WebTest.Context.ContainsKey(WebTestUserMonitor.LOGIN) ? (bool)e.WebTest.Context[WebTestUserMonitor.LOGIN] : true;


            bool isLastLoginRequest = e.WebTest.Context.ContainsKey(WebTestUserMonitor.IS_LAST_LOGIN_REQUEST) ? (bool)e.WebTest.Context[WebTestUserMonitor.IS_LAST_LOGIN_REQUEST] : false;


 


            // if login = true && this is the last login request


            if (login && isLastLoginRequest)


            {


                WebTestUserMonitor.Instance.login_PostRequest(sender, e);


                return;


            }          


        }


    }


}


 


LoadTestPlugin.cs


 


using System;


using System.Collections.Generic;


using System.Text;


using Microsoft.VisualStudio.TestTools.LoadTesting;


 


namespace TestMixPlugins


{


    public class LoadTestPlugin : ILoadTestPlugin 


    {


        public void Initialize(LoadTest loadTest)


        {


            m_loadTest = loadTest;


            m_loadTest.LoadTestStarting += new EventHandler(m_loadTest_LoadTestStarting);


            m_loadTest.LoadTestFinished += new EventHandler(m_loadTest_LoadTestFinished);


        }


 


        void m_loadTest_LoadTestStarting(object sender, EventArgs e)


        {


            WebTestUserMonitor.Instance.Start();


        }


 


        void m_loadTest_LoadTestFinished(object sender, EventArgs e)


        {


            WebTestUserMonitor.Instance.Stop();


        }


 


        private static LoadTest m_loadTest;


    }


}


 


 


VUserInfo.cs


using System;


using System.Collections.Generic;


using System.Text;


 


namespace TestProject1


{


    class VUserInfo


    {


        // This class stores a vuser’s information during test execution


        public VUserInfo(int userId)


        {


            m_userId = userId;


            m_numTestExecuted = 0;


            m_loginContext = new Dictionary<string, object>();


            m_testList = new List<string>();


        }


 


        public int UserId


        {


            get { return m_userId; }


        }


 


        // Number of tests that have been completed by the user


        public int NumTestExecuted


        {


            get { return m_numTestExecuted; }


            set { m_numTestExecuted = value; }


        }


 


        // Web test context needed to issue non-login requests for a return user


        public Dictionary<string, object> LoginContext


        {


            get { return m_loginContext; }


            set { m_loginContext = value; }


        }


 


        public List<string> TestList


        {


            get { return m_testList; }


        }


 


        private int m_userId;


        private int m_numTestExecuted;


        private Dictionary<string, object> m_loginContext;


        private List<string> m_testList;


    }


}


    


WebTestUserMonitor.cs


 


using System;


using System.IO;


using System.Text;


using System.Collections.Generic;


using Microsoft.VisualStudio.TestTools.WebTesting;


 


namespace TestMixPlugins


{


    public class WebTestUserMonitor


    {


        public static WebTestUserMonitor Instance


        {


            get { return s_instance; }


        }


 


        public void PreWebTest(object sender, PreWebTestEventArgs e)


        {


            int userId = e.WebTest.Context.WebTestUserId;


            bool isNewUser = e.WebTest.Context.IsNewUser;


            bool login = false;


 


            lock (s_usersLock)


            {


                if (!s_users.ContainsKey(userId))


                {


                    s_users.Add(userId, new VUserInfo(userId));


                }


 


                int numTestExecuted = s_users[userId].NumTestExecuted;


 


                // if this is a new user, enable login


                // if this is a returning user, add the login context needed for the test


                if (isNewUser || numTestExecuted == 0)


                {


                    login = true;


                }


                else


                {


                    e.WebTest.Context.Add(LOGIN_CONTEXT, s_users[userId].LoginContext);


                }


            }


 


            e.WebTest.Context.Add(LOGIN, login);


        }


 


        public void PostWebTest(object sender, PostWebTestEventArgs e)


        {


            int userId = e.WebTest.Context.WebTestUserId;


            string testName = e.WebTest.Name;


 


            lock (s_usersLock)


            {


                s_users[userId].NumTestExecuted++;


                s_users[userId].TestList.Add(testName);


 


                if (e.WebTest.Context.IsNewUser)


                {


                    s_users.Remove(userId);


                }


                else


                {


                    // save the login context for future usage


                    if ((bool)e.WebTest.Context[LOGIN])


                    {


                        s_users[userId].LoginContext = (Dictionary<string, object>)e.WebTest.Context[LOGIN_CONTEXT];


                    }


                }


 


                bool login = e.WebTest.Context.ContainsKey(LOGIN) ? (bool)e.WebTest.Context[LOGIN] : true;


                if (login)


                {


                    s_numOfLoginTest++;


                }


            }


        }


 


        public void login_PreRequest(object sender, PreRequestEventArgs e)


        {


            bool login = e.WebTest.Context.ContainsKey(LOGIN) ? (bool)e.WebTest.Context[LOGIN] : true;


            bool isFirstLoginRequest = true;


            bool isLastLoginRequest = true;


            bool isLoginRequest = true;


 


            isFirstLoginRequest = e.Request.Url.Contains(“LoginRequestFirst”);


            isLastLoginRequest = e.Request.Url.Contains(“LoginRequestLast”);


            isLoginRequest = e.Request.Url.Contains(“LoginRequest”);


 


            // remove the suffix from the request


            if (isLoginRequest)


            {


                int endPos = e.Request.Url.IndexOf(“LoginRequest”);


                if (endPos > 0)


                {


                    e.Request.Url = e.Request.Url.Substring(0, endPos);


                }


            }


 


            if (login)


            {


                if (isFirstLoginRequest)


                {


                    // copy the context before the first login request is issued


                    KeyValuePair<string, object>[] contextBeforeLogin = new KeyValuePair<string, object>[e.WebTest.Context.Count];


                    e.WebTest.Context.CopyTo(contextBeforeLogin, 0);


                    e.WebTest.Context.Add(CONTEXT_BEFORE_LOGIN, contextBeforeLogin);


                }


 


                if (isLastLoginRequest)


                {


                    e.WebTest.Context.Add(IS_LAST_LOGIN_REQUEST, true);


                }


 


                return;


            }


 


            // login is not needed, skip the login requests


            if (isLoginRequest)


            {


                e.Instruction = WebTestExecutionInstruction.Skip;


            }


 


            if (isFirstLoginRequest)


            {


                // Copy the Login context required for the rest of the test


                int count = ((Dictionary<string, object>)e.WebTest.Context[LOGIN_CONTEXT]).Count;


                KeyValuePair<string, object>[] loginContext = new KeyValuePair<string, object>[count];


                ((ICollection<KeyValuePair<string, object>>)e.WebTest.Context[LOGIN_CONTEXT]).CopyTo(loginContext, 0);


                System.Collections.IEnumerator ienum = loginContext.GetEnumerator();


 


                while (ienum.MoveNext())


                {


                    KeyValuePair<string, object> keyValuePair = (KeyValuePair<string, object>)ienum.Current;


                    e.WebTest.Context.Add(keyValuePair.Key, keyValuePair.Value);


                }


            }


        }


 


        public void login_PostRequest(object sender, PostRequestEventArgs e)


        {


            // this is the last login request


            Dictionary<string, object> contextBeforeLogin = new Dictionary<string, object>();


            Dictionary<string, object> contextChangedByLogin = new Dictionary<string, object>();


 


            System.Collections.IEnumerator ienum = ((KeyValuePair<string, object>[])e.WebTest.Context[CONTEXT_BEFORE_LOGIN]).GetEnumerator();


            while (ienum.MoveNext())


            {


                KeyValuePair<string, object> keyValuePair = (KeyValuePair<string, object>)ienum.Current;


                contextBeforeLogin.Add(keyValuePair.Key, keyValuePair.Value);


            }


 


            // compare the contexts before and after login, get the context changed by the login


            foreach (KeyValuePair<string, object> keyValuePair in e.WebTest.Context)


            {


                string key = keyValuePair.Key;


                object value1 = keyValuePair.Value;


 


                if (contextBeforeLogin.ContainsKey(key))


                {


                    object value2 = contextBeforeLogin[key];


                    if (value1.ToString().ToUpper().CompareTo(value2.ToString().ToUpper()) != 0)


                    {


                        contextChangedByLogin.Add(key, value1);


                    }


                }


                else


                {


                    contextChangedByLogin.Add(key, value1);


                }


            }


 


            e.WebTest.Context.Add(LOGIN_CONTEXT, contextChangedByLogin);


            e.WebTest.Context.Remove(IS_LAST_LOGIN_REQUEST);


        }


 


        public void Start()


        {


            FileInfo file = new FileInfo(m_logFile);


            if (file.Exists)


            {


                file.Delete();


            }


        }


 


        public void Stop()


        {


            lock (s_usersLock)


            {


                File.AppendAllText(m_logFile, “Number of login tests executed: “ + s_numOfLoginTest.ToString());


            }


        }


 


        public static readonly string LOGIN = “Login”;


        public static readonly string IS_LAST_LOGIN_REQUEST = “IsLastLoginRequest”;


        private const string CONTEXT_BEFORE_LOGIN = “ContextBeforeLogin”;


        private const string LOGIN_CONTEXT = “LoginContext”;


        private readonly string m_logFile = “d:\\result.txt”;


        private static object s_usersLock = new object();


        private static int s_numOfLoginTest = 0;


        private static WebTestUserMonitor s_instance = new WebTestUserMonitor();


        private static Dictionary<int, VUserInfo> s_users = new Dictionary<int, VUserInfo>();


    }


}


 



=====================================================================


B. To do this using coded web tests:



  1. Create a new test project.
  2. Add VUserInfo.cs and WebTestUserMonitor.cs to the project.
  3. Create the web tests. Each web test should include the same set of login request(s).
  4. Generate coded Web tests from the existing ones.
  5. Modify the coded Web tests as suggested in the following code sample.
  6. Add the coded Web tests to a load test.

   


WebTestUserMonitor.cs


using System;


using System.IO;


using System.Text;


using System.Collections.Generic;


using Microsoft.VisualStudio.TestTools.WebTesting;


 


namespace TestProject1


{


    public class WebTestUserMonitor


    {


        public WebTestUserMonitor()


        {


        }


 


        public static WebTestUserMonitor Instance


        {


            get { return s_instance; }


        }


 


        public void PreWebTest(object sender, PreWebTestEventArgs e)


        {


            int userId = e.WebTest.Context.WebTestUserId;


            // Is this a new user


            bool isNewUser = e.WebTest.Context.IsNewUser;


            bool login = false;


 


            lock (s_usersLock)


            {


                if (!s_users.ContainsKey(userId))


                {


                    s_users.Add(userId, new VUserInfo(userId));


                }


 


                int numTestExecuted = s_users[userId].NumTestExecuted;


 


                // If this is a new user, enable login


                // If this is a return user, add the login context needed for the test


                if (isNewUser || numTestExecuted == 0)


                {


                    login = true;


                }


                else


                {


                    e.WebTest.Context.Add(LOGIN_CONTEXT, s_users[userId].LoginContext);


                }


            }


 


            e.WebTest.Context.Add(LOGIN, login);


        }


 


        public void PostWebTest(object sender, PostWebTestEventArgs e)


        {


            int userId = e.WebTest.Context.WebTestUserId;


            string testName = e.WebTest.Name;


 


            lock (s_usersLock)


            {


                s_users[userId].NumTestExecuted++;


                s_users[userId].TestList.Add(testName);


 


                if (e.WebTest.Context.IsNewUser)


                {


                    s_users.Remove(userId);


                }


                else


                {


                    // save the login context for future usage


                    if ((bool)e.WebTest.Context[LOGIN])


                    {


                        s_users[userId].LoginContext = (Dictionary<string, object>)e.WebTest.Context[LOGIN_CONTEXT];


                    }


                }



            }


        }


 


        public static void login_PreRequest(object sender, PreRequestEventArgs e)


        {


            KeyValuePair<string, object>[] contextBeforeLogin =


                          new KeyValuePair<string, object>[e.WebTest.Context.Count];


 


            // Get a copy of the context before the login request is issued


            e.WebTest.Context.CopyTo(contextBeforeLogin, 0);


            e.WebTest.Context.Add(CONTEXT_BEFORE_LOGIN, contextBeforeLogin);


        }


 


        public static void login_PostRequest(object sender, PostRequestEventArgs e)


        {


            Dictionary<string, object> contextBeforeLogin =


                                                 new Dictionary<string, object>();


            Dictionary<string, object> contextChangedByLogin =


                                                 new Dictionary<string, object>();


 


            System.Collections.IEnumerator ienum =  ((KeyValuePair<string,object>[])e.WebTest.Context[CONTEXT_BEFORE_LOGIN]).GetEnumerator();


            while (ienum.MoveNext())


            {


                KeyValuePair<string, object> keyValuePair =


                                           (KeyValuePair<string, object>)ienum.Current;


                contextBeforeLogin.Add(keyValuePair.Key, keyValuePair.Value);


            }


 


            // Compare the contexts before and after login, and


            // get the context changed by the login


            foreach (KeyValuePair<string, object> keyValuePair in e.WebTest.Context)


            {


                string key = keyValuePair.Key;


                object value1 = keyValuePair.Value;


 


                if (contextBeforeLogin.ContainsKey(key))


                {


                    object value2 = contextBeforeLogin[key];


                    if (value1.ToString().ToUpper().CompareTo(value2.ToString().ToUpper()) != 0)


                    {


                        contextChangedByLogin.Add(key, value1);


                    }


                }


                else


                {


                    contextChangedByLogin.Add(key, value1);


                }


            }


 


            e.WebTest.Context.Add(LOGIN_CONTEXT, contextChangedByLogin);


        }


 


        private const string CONTEXT_BEFORE_LOGIN = “ContextBeforeLogin”;


        private const string LOGIN_CONTEXT = “LoginContext”;


        private const string LOGIN = “Login”;


        private static object s_usersLock = new object();


        private static WebTestUserMonitor s_instance = new WebTestUserMonitor();


        private static Dictionary<int, VUserInfo> s_users =


                                               new Dictionary<int, VUserInfo>();


    }


}


 


WebTestCoded.cs


namespace TestProject1


{


    using System;


    using System.Collections.Generic;


    using System.Text;


    using TestMixPlugins;


    using Microsoft.VisualStudio.TestTools.WebTesting;


    using Microsoft.VisualStudio.TestTools.WebTesting.Rules;


 


 


    public class WebTest2Coded : WebTest


    {


        public WebTest2Coded()


        {


            this.PreAuthenticate = true;


 


            //—————————————


            // Modification 1


            //—————————————


            // Add the following two lines to your coded web test


            this.PreWebTest += new EventHandler<PreWebTestEventArgs>(WebTestUserMonitor.Instance.PreWebTest);


            this.PostWebTest += new EventHandler<PostWebTestEventArgs>(WebTestUserMonitor.Instance.PostWebTest);


        }


 


        public override IEnumerator<WebTestRequest> GetRequestEnumerator()


        {


            //—————————————


            // Modification 2 – Login Section


            //—————————————


            bool login = Context.ContainsKey(“Login”) ? (bool)Context[“Login”] : true;


 


            // Incorporate the following if-else block into the login section


            if (login)


            {


 


                WebTestRequest loginRequest = new WebTestRequest(“http://server/site/login.aspx”);


                ExtractHiddenFields rule1 = new ExtractHiddenFields();


                rule1.ContextParameterName = “1”;


                loginRequest.ExtractValues += new EventHandler<ExtractionEventArgs>(rule1.Extract);


               


                // Insert the PreRequest and PostRequst handlers for the login request


                loginRequest.PreRequest += new EventHandler<PreRequestEventArgs>(WebTestUserMonitor.login_PreRequest);


                loginRequest.PostRequest += new EventHandler<PostRequestEventArgs>(WebTestUserMonitor.login_PostRequest);


                yield return loginRequest;


 


            }


            else


            {


                // Copy the Login context required for the test


                int count = ((Dictionary<string, object>)Context[“LoginContext”]).Count;


                KeyValuePair<string, object>[] loginContext = new KeyValuePair<string,object>[count];


                ((ICollection<KeyValuePair<string, object>>)Context[“LoginContext”]).CopyTo(loginContext, 0);


                System.Collections.IEnumerator ienum = loginContext.GetEnumerator();


 


                while (ienum.MoveNext())


                {


                    KeyValuePair<string, object> keyValuePair = (KeyValuePair<string, object>)ienum.Current;


                    Context.Add(keyValuePair.Key, keyValuePair.Value);


                }


            }


            // End of Modification 2


 


            WebTestRequest request1 = new WebTestRequest(“http://server/site/page1.aspx”);


            yield return request1;


 


            WebTestRequest request2 = new WebTestRequest(“http://server/site/page2.aspx”);


            yield return request2;


 


            WebTestRequest request3 = new WebTestRequest(“http://server/site/page3.aspx”);


            yield return request3;


 


        }


    }


}


 

Comments (1)

  1. Martin Kulov says:

    Could you provide more detailed explanation for this code?