Stressless Load Testing

All About Performance Testing & Tools for Web, Mobile and API

Load Testing Dynamics CRM / 365 with LoadRunner


This post, is a reprint of the article originally published in the  Dynamics CRM Performance Testing blog hosted on Microsoft Dynamics community.


Tried unsuccessfully to test the performance of Microsoft Dynamics CRM/365 scenarios with LoadRunner? Find out the core reasons for your script errors and how to fix them.

1. Introduction:

Today we continue the discussion about Performance Testing Dynamics CRM / 365 using popular load testing tools. In this post the focus is on LoadRunner. We will explore some of the challenges of developing scripts in LoadRunner and how to overcome them using correlation rules unveiled in the previous post.

LoadRunner has one of the best autocorrelation engine. However, when comes to developing scripts for Microsoft Dynamics products, it has some shortcomings, and substantial amount of manual correlation is required.

In spite of multiple questions regarding feasibility of load testing Dynamics CRM with LoadRunner posted on numerous forums, we could not find publications demonstrating such LoadRunner capability. The purpose of this blog is to make a case for using LoadRunner for performance testing Dynamics CRM and sharing some of the best practices in developing scripts.

2. Script Creation Using LoadRunner

We will used the same on-premise Dynamics CRM instance and will work with the same scenario as in the previous post. Let's get started with the recording part. We assume that readers of this blog are familiar with the basic operation of LoadRunner.

STEP 1. Recording the Application Flow Using LoadRunner:

  1. After launching the VuGen, click on Add New Script. There, select the web (http/html) protocol and provide a location where you want to save the script.

  2. Press CTRL+R to launch the record window. Here, select the proper browser and provide the application URL. Then click on the recording option.

  3. Select the URL mode of recording instead of HTML mode. This is because Dynamics CRM sends many Ajax requests which might be not captured in HTML mode.

  4. From the code-generation section of the recording option, select the Correlation Scan to enable the auto-correlation feature of LoadRunner, as shown in the screenshot below.



  5. We will record the same test scenario of creating a lead as was discussed in the previous blog. It consists of the following steps:
    1. Login to open Dashboard
    2. Navigate to Sales –> Leads
    3. Click New
    4. Complete the lead form and Save

  6. Once recording is finished, LoadRunner will display the list of correlation parameters it has automatically captured. For this application, LoadRunner has captured 46 correlation parameters.




STEP 2. Developing the script With Auto-Correlation:

  1. LoadRunner gives the option to choose what parameter need to be auto-correlated. Because a typical user would not know what parameters to use and which to disable, we see what would happen if we apply the auto-correlation rule for every parameter.

    To do this, just press CTRL+A, select the "Correlate" button. The status will be changed from 'New' to 'Applied'.



  2. Let's run the script now to see if it succeeds after replacing all the values with the auto-correlation parameters suggested by LoadRunner.

    Note: To run any CRM application script from LoadRunner, check "Use WinInet replay instead of Sockets (Windows Only)" from Runtime Settings, as by default LoadRunner doesn't support NTLM authentication used in this on-premise instance.



  3. While replaying the script, it has been observed that most of the auto-correlation failed because the extractors were not designed properly. Let's pick one and see why it's failing.

    The following is one of the parameters:

    web_reg_save_param_regexp(
    "ParamName=CorrelationParameter_3",
    "RegExp=QuickCreateTimestamp\":\"\\{\\\\\"formts\\\\\":\\\\\"444678\\\\\",\\\\\"mdts\\\\\":\\\\\
    "489035\\\\\",\\\\\"userts\\\\\":\\\\\"131429614610738021\\\\\",\\\\\"businessRulesVersion\\\\\":
    \\\\\"(.*?)\\\\\"}"
    ,
    SEARCH_FILTERS,
    "Scope=Body",
    "IgnoreRedirections=No",
    LAST);

    LoadRunner uses regular expression to capture the required parameter value.



    The following is the http request before which "CorrelationParameter_3" has been placed:

    web_url("main.aspx",
    "URL=https://crm.client-centrix.com:4433/crmdb/main.aspx",
    "Resource=0",
    "RecContentType=text/html",
    "Referer=https://crm.client-centrix.com:4433/",
    "Snapshot=t20.inf",
    "Mode=HTTP",
    LAST);

    "CorrelationParameter_3" parameter has been placed before the "main.aspx" request, so it will be extracted from the http response of this request. Now let's see the replayed response of this request. To view the recorded & replayed response of any particular request, right click on that request and click on 'Show Snapshot'.



    To compare both record & replay logs, click on the split button in snapshot section. Go to the Response Body for both of these (replayed & recorded session).

    Now search the Left Boundary: "QuickCreateTimestamp":"{\"formts\":\"444678\",\"mdts\":\"489035\",\"userts\":\"131429614610738021\",\"businessRulesVersion\":\"" string in the response body of both replayed & recorded session. In the recorded response, we are able to see this string. But when we search the same string in the replayed response, we were getting no results. Let's have a look at the below screenshot.



    This is because the "userts\":\"131429614610738021" value was changed during replay. After replaying the script, we observed that this value has been changed to userts\":"\"131433700013419878". But as LoadRunner just put the hard-coded value during auto-correlation, the script has failed. Likewise, we have observed that lots of auto-correlation parameters just doesn't work while replaying because the extractor (Regular Expression) was not properly designed.

    Apart from this, LoadRunner has recorded a lot of auto-correlation parameters which are not required. The following is a screenshot which lists all the correlations that are excessive.



    At the same time, LoadRunner didn't record various important correlations which are necessary for Dynamic CRM Application to make a successful script. We will discuss this in the next section.

3. Reusing StresStimulus Correlation Rules

STEP 3. Developing the Script in LoadRunner using correlation rules from StresStimulus:

  1. After recording the same flow using StresStimulus, as was mentioned above, all the necessary parameters were correlated automatically without any manual intervention. The script was executed successfully and a new record has been created in the CRM application.



  2. While analyzing the StresStimulus script, we discovered several correlation parameters required for replaying the script successfully. Along with these parameters, a few request headers were also required. Once these correlations and headers were added in the LoadRunner script, it replayed successfully as well.

    List Of Correlation Parameters (extractors) & their corresponding HTTP requests:
    1. Correlation Parameter:
      web_reg_save_param_ex(
      "ParamName=userts",
      "LB==",
      "RB=';\r\nvarChangeDoubleQuoteToSingleQuote",
      SEARCH_FILTERS,
      "Scope=Body",
      "IgnoreRedirections=No",
      LAST);

      Request Name:
      web_url("crm.client-centrix.com:4433",
      "URL=https://crm.client-centrix.com:4433/",
      "Resource=0",
      "RecContentType=text/html",
      "Referer=",
      "Snapshot=t6.inf",
      "Mode=HTTP",
      LAST);

    2. Correlation Parameter:
      1. web_reg_save_param("CRMWRPCTokenTimeStamp","LB=tokenData\":[{\"Timestamp\":\"","RB=\",\"Token",LAST)
      2. web_reg_save_param("CRMWRPCToken","LB=Token\":\"","RB=\",\"Url",LAST)
      3. web_reg_save_param("ReferReqID","LB=REQ_ID:","RB=\n",LAST)
      The first two correlations ""main.aspx"CRMWRPCTokenTimeStamp" and "CRMWRPCToken" are really necessary for any Dynamic CRM Applications. These two parameters need to be passed in the request headers of various requests.

      Request Name:
      web_url("Data.aspx_2",
      "URL=https://crm.cIient-centrix.com:4433/crmdb/form/Data.aspx?_CreateFromId=&CreateFrom_Type=&counter=1498488330063&etC 24&extraqs=",
      "Resource=0",
      "RecContentType=text/html",
      "Snapshot=t334.inf",
      "Mode=HTTP",
      LAST);

    3. Correlation Parameter:
      web_reg_save_param("EntityID","LB=entity\":{\"Id\":\"{","RB=}\",\"TypeCode",LAST);

      Request Name:
      web_custom_request("InlineEditWebService.asmx",
      "URL=https://crm.client-centrix.com:4433/crmdb/AppWebServices/InlineEditWebService.asmx",
      "Method=POST",
      "Resource=0",
      "RecContentType=text/html",
      "Snapshot=t395.inf",
      "Mode=HTTP",
      "EncType=text/xml; charset=UTF-8",
      "Body==\"http://schemas.xmlsoap.org/soap/envelopeA" xmlns:xXMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/X</id>&# 60;name>lead</name><formId>{formid}</formId><data&#62;1&#60;&#47;statuscode&#62;&#60;donotbulkemail&#62;< /associations></Input>false",
      LAST
      );
      Note: A portion of this request has been removed as it is too big.

  3. Apart from these necessary correlation parameters, LoadRunner also missed a few important headers in several requests. The following is the list of headers and their corresponding requests.

    List of Headers & Their Corresponding HTTP Requests:
    1. Header:
      web_add_header("SOAPAction","http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute");

      Request Name:
      web_custom_request("web",
      "URL=https://crm.cIient-centrix.com:4433/crmdb/XRMServices/ZOI1/Organization.svc/web",
      "Method=POST",
      "Resource=0",
      "RecContentType=text/html",
      "Snapshot=t196.inf",
      "Mode=HTTP",
      "EncType=text/xml; charset=UTF-8",
      "Body==http://schemas.microsoft.com/xrm/2011/Contracts/Services\" xmlns:i=\"http://www.w3.org/2001/XM LSchema-instance\" xmlnszc=http://www.w3.org/2001/XM LSchema\">Query:key>fetch version='1.0' output-format='me-platform' mapping='Iogical'distinct='faIse'><entity name='msdyn_waIIsavedqueryusersettings'><attribute name='msdyn_waIIsavedqueryusersettingsid' /> <attribute name='msdyn_waIlsavedqueryid' /><attribute name='msdyn_savedqueryname'/><attribute name='msdyn_entityname' /><attribute name='msdyn_otc' /></fiIter></entity>/fetch>:vaIue>RetrieveMuItipIe",
      LAST);

      Note: A portion of this request has been removed as it is too big.

    2. Header:

      The extracted value of "CRMWRPCTokenTimeStamp", CRMWRPCToken", and "ReferrerReqId" correlation parameters need to be passed on to the headers of several requests.

      1. web_add_header("CRMWRPCTokenTimeStamp","{CRMWRPCTokenTimeStamp}");
      2. web_add_header("CRMWRPCToken","{CRMWRPCToken1}");
      3. web_add_header("ReferrerReqId","{ReferReqID}");

      Request Name:
      web_custom_request("lnIineEditWebService.asmx",
      "URL=https://crm.cIient-centrix.com:4433/crmdb/AppWebServices/InlineEditWebService.asmx",
      "Method=POST",
      "Resource=0",
      "RecContentType=text/html",
      "Snapshot=t395.inf",
      "Mode=HTTP",
      "EncType=text/xml; charset=UTF-8",
      "Body==http://schemas.microsoft.com/xrm/2011/Contracts/Services\" xmlns:i=\"http://www.w3.org/2001/XM LSchema-instance\" xmlnszc=http://www.w3.org/2001/XM LSchema\">Query:key>fetch version='1.0' output-format='me-platform' mapping='Iogical'distinct='faIse'> <entity name='msdyn_waIIsavedqueryusersettings'><attribute name='msdyn_waIIsavedqueryusersettingsid' /> <attribute name='msdyn_waIlsavedqueryid' /><attribute name='msdyn_savedqueryname'/><attribute name='msdyn_entityname' /><attribute name='msdyn_otc' /><attribute name='msdyn_entitydisplayname'47;><attribute name='msdyn_isvisible' /><attribute name='msdyn_isvirtual'47;><attribute name='msdyn_default' /><filter type='and'>60;condition attribute='msdyn_userid' operator='eq-userid' /> <condition attribute='msdyn_isvirtual' operator='eq' value='1' /> <condition attribute='msdyn_isvisible'operator='eq' value='1' /> <condition attribute='msdyn_isfollowing' operator='eq' value='1' /> <condition attribute='msdyn_includewaIIinresponse' operator='eq'value='1' /></fiIter> </entity>/fetch>:vaIue>RetrieveMuItipIe",
      LAST);

      Note: A portion of this request has been removed as it is too big.

STEP 4. How to do Manual Correlation in LoadRunner:

Let's consider that we need to do manual correlation of "CRMWRPCToken". Let's go step by step to handle this.
  1. Search for the recorded value (mBNnr1djEeeAzAAVXQKjAERasmclN2SzOtgj1AMTEn6aFWQTEu3Io3BnX/i7/5az) of "CRMWRPCToken" in the code generation log of the Output section.



  2. Once the search string is visible in the code generation log, we need to make sure of two things:
    • The required value gets picked up from either the Response Body or Response Header, not from Request Body/Header.
    • It needs to be picked up from the response of any of the previous requests, where the recorded value is going to be replaced with the correlation parameter.
    • Capture the Left and Right Boundary of the required value from the response.
    • Scroll down until you find "$$$$$$ Response Body For Transaction With Id (IDNumber) Ended $$$$$$".
    • Scroll down further to see "****** Add Event For Transaction With Id (IDNumber ******", then the actual request.
    • If both these IDNumbers match, then the required value needs to be picked up from the response of this particular request.
    • In this case, the request from which response, the "CRMWRPCToken" needs to be picked up is as follows:
      web_url("Data.aspx_2",
      "URL=https://crm.client-centrix.com:4433/crmdb/form/Data.aspx?_CreateFromId=&_CreateFromType=&counter=1498488330063&etc=4&extraqs=%3fetc%3d4&formid=&oid=&pagemode=iframe&pagetype=entityrecord&process=",
      "Resource=0",
      "RecContentType=text/html",
      "Snapshot=t334.inf",
      "Mode=HTTP",
      LAST);

    • Place the web_reg_save_param("CRMWRPCToken","LB=Token\":\"","RB=\",\"Url",LAST); function before the above request to capture the token.
    • In LoadRunner, even correlating every necessary value and adding all required custom headers was not enough for this application, as the script still broke. After a bit more investigation, it had been found out that the dynamic value of the "CRMWRPCToken" parameter might contain zero or one or more backslashes. For every backslash (/) character, there will be an escape character (\). But in the actual request header, the dynamic value is getting passed without any escape character (\). Hence, we needed to write one custom function to delete the escape character (\) from the actual value. Here is a snippet:
      char* del(char str[], char ch) {
      int i, j = 0;
      int size;
      char ch1;
      char str1[100];
      size = strlen(str);
      for(i = 0; i < size; i++) {
      if (str[i] != ch) {
      ch1 = str[i]
      str1[j] = ch1;
      j++;
      }
      }
      str1[j] = '\0';
      return str1;}

    • In StresStimulus, we do not need to write any custom functions because there is a feature called "Use Escaped Hex Decoding" for every extractor which will be used to handle such situation.

4. Conclusion

As was demonstrated on the simple test scenario, LoadRunner autocorrelation capabilities do not support recorded Dynamics CRM scripts out-of-the-box. Some of LoadRunner parameters must be removed as they break the script. Some other parameters are missing and must be created manually. Manual correlation from scratch is difficult as there are many unknown extractors and parameters that should be created. However using information about required extractors and parameters, exposed by StresStimulus, substantially expedited scripting in LoadRunner. We also found that some missing custom headers have to be added and that some custom functions are required to make script to work.

If your Dynamics CRM/365 scenarios require any additional rules not discussed in this blog, expose them by recording them in StresStimulus, available here. Then use these rules for guidance to manually create extractors and parameters in LoadRunner and make your performance test finally work.

The main part of this blog is written by a guest blogger Amit Gorain. Amit is an expert in performance testing Enterprise applications with LoadRunner and JMeter. He is the host of "All About Performance Testing" blog and the owner of a YouTube channel on load testing.
blog comments powered by Disqus