Thursday, July 5, 2012

Load-Testing ASP.NET MVC (Part 3 - JMeter (slightly more) advanced)

Part 1 - Apache Workbench
Part 2 - JMeter introduction
Part 3 - JMeter (slightly more) advanced

As promised, in this post I'm going to explain some more JMeter stuff. So, I'll show how to:
  • Submit information (HTTP POST)
  • Handle authentication
    • Particularly in the ASP.NET MVC context
  • Read information from a page and store it in a variable
    • Particularly to read the ASP.NET MVC anti-forgery token
  • Vary the posted data by reading it from a CSV file
Also, I'll include some extra elements to make the test more maintainable and professional.

So, again, lots of ground to cover so lets get started.

First, we need to create the application that will be tested. It'll be pretty simple: a login screen and a form with a bunch of fields. After submitting it should show a success or error message according to the validation.

So, this is the login screen:


After supplying the correct credentials (meaning, password = "password") a page is opened with 2 fields ready to submit feedback.



The page has validation set up

And after filling the two fields correctly a message appears and the form fields are cleared so that new feedback may be supplied.
That's it. Now, there's some small details to take into consideration.

  • The web page has Forms Authentication. Pretty standard stuff: if the credentials are valid then create the authentication cookie.
  • I'm using an ASP.NET MVC mechanism to defend against Cross-Site Request Forgery by supplying a token in the HTML Form that must be sent on the POST. This is done by using the ActionFilter [ValidateAntiForgeryToken] on the action and a @Html.AntiForgeryToken on the view. So, if a POST request does not include the anti-forgery token the server will reject the request and throw an exception. 
Let's create a pseudo-representation for our test-plan:
For each User
 |
 +--- Login
 +--- Repeat n times
         |
         +--- Submit Feedback
         +--- Wait a couple of seconds before submitting again
This will roughly translate to:



Now, as this is the second post on JMeter I'm going to jump some hoops and show the final test plan and then explain each of the elements.


This might seem a little scary but most of the elements should be really easy to grasp. I'm going to show the configuration for each one:

Test Configuration

It's really useful to have all the configuration in one spot, typically at the start of the test. This way we can tweak stuff more easily.



HTTP Request Defaults

Instead of repeating the server and port on each request we can define defaults for those values. This is really a time-saver, particularly when switching environments.


HTTP Cookie Manager

The cookie manager is the only thing required to handle forms authentication. It makes sure that the authentication cookie is there, making your requests authorized after the login.


For Each User

Normal thread, but using variables instead of hardcoded numbers. The Loop Count is 1 because we have a Loop Controller inside.


Login

The login is a simple post to a specific action on our website. The username is always supplied as "DummyUser" with a "password" password.

Notice that the server name and port number are empty, as we're using the HTTP request defaults.


Open Feedback Page

Just opening the feedback page before submitting. This is required because of the next action. 


Regular Expression Extractor

The Request verification token is a hidden value in the form. Something like:
<input name="__RequestVerificationToken" type="hidden" value="........"/>

We have to load that field into a JMeter variable. Thus we use a Regular Expression Extractor, passing the following regular expression:

name="__RequestVerificationToken" type="hidden" value="([A-Za-z0-9+=/\-\_]+?)"


Repeat

Pretty self-explanatory. Repeat n times for each user (thread).


Load data from file

It's a good idea to have diversity on your tests. Thus, instead of having hardcoded values, we can read values from a CSV file and assign them to variables. These may then be used normally in JMeter.

For example, I've created a csv file, named "Feedback.csv" with this content:

Nice Site;I really enjoyed that movie
Pretty good;I liked it, but there's some stuff that I didn't really getAwful;This is the worst piece of **** I've ever seen
Interesting;Nice stuff
(...)
much more lines. Should be enough to fulfill the number of users * number of requests.

One thing to note is the Sharing mode. With "All threads" it means that each request for each user will choose a new value from the csv file.



Submit Feedback

This is where the feedback itself is submitted. Notice that all the values use variables and that the Request Verification Token is also sent.


Check Result

Just checking if the response shows a success message or not.



Wait Before Next Request

If we're simulating real users, we want to wait a little bit before issuing another request. I like the Gaussian Random Timer a lot because the value is a "controlled random" as it follows a normal (Gaussian) distribution on the test-plan as a whole.


The scary part is that JMeter provides much more stuff than what I've shown here. Anyway, I'll wrap up things for now. If you've got any doubt/suggestion place a comment.

25 comments:

  1. very neat & detailed article. can you add something which is more specific to MVC apps?

    ReplyDelete
  2. Not really. The only thing more particular to ASP.NET MVC is the usage of the Request Verification Token. Everything else should be straight HTTP.

    ReplyDelete
  3. Excellent Post. Also visit http://whiteboxqa.com/#performance.php

    ReplyDelete
  4. great work......struggling for last 1 week......got fully clarified with your guidance material.....very greatfull thanks for you.

    ReplyDelete
  5. after logging in i want to show my home page which is a jsp page......

    ReplyDelete
    Replies
    1. Shouldn't be any difference there. Just create an HTTP Request (GET) for that page

      Delete
  6. i cant get page exactly..i am getting the page without css,img and js.how to get the exact page.

    ReplyDelete
  7. I'm using jmeter 2.10 r1533061 and I'm trying to capture the RequestValidationToken using the reg-ex and then use it in my next call.
    Rather than recognising $(RequestVerificationToken) as a variable, it's literally sending as =$(RequestVerificationToken).

    Am I doing something daft here?

    ReplyDelete
    Replies
    1. Are you using Brackets {}?

      Delete
    2. Im facing the same issue where it passes the parameter name instead of the extracted value from RegEx in RequestVerificationToken.

      I'm using curly braces '{}'.

      In Regular Expression Extractor these are the entered details.>
      Reference Name : REQUEST_VERIFICATION_TOKEN
      Reg Expression : input name="__RequestVerificationToken" type="hidden" value="([A-Za-z0-9+=/\-\_]+?)"
      Template : $1$
      Match No. : 1
      Default Value : (blank)

      In HTTP Request these are the entered details. >
      Password : ${Password}
      TenantName : ${TenantName}
      Username : ${Username}
      __RequestVerificationToken : ${REQUEST_VERIFICATION_TOKEN

      The Request which passes as displays in View Result Tree >
      POST data:
      Password=123456&TenantName=tenant&Username=admin&__RequestVerificationToken=%24%7BREQUEST_VERIFICATION_TOKEN%7D


      Can someone assist me of why the RequestVerificationToken is not extracting?

      Delete
  8. i have different users in my web project such as admin,user,receptionist,etc.,when i login as admin it should show only some menu items and as user means it should show only some menu items...how to test these through assertion in jmeter

    ReplyDelete
  9. Hi, I think your blog post is the only one I can find on ASP .NET MVC...

    I have followed your steps but for my webpage and removed the feedback part. I just want to be able to log in my webpage but I'm getting a 500 response code. My post data included the verificationtoken, username, password and the language (I need it from the login page) and I just get a server error. Strangely, under Post Data after the huge text of what post data I sent, It says [no cookies] underneath. Could that be it?

    ReplyDelete
    Replies
    1. Well, it's normal that the first request won't have cookies. Afterwards, after a successful login, they should appear on additional requests. Regarding the 500, not really sure. You'll have to check the server logs.

      I would suggest that you use Fiddler (or even the regular network tab of the browser) and try to mimic as much as possible the POST request that you're seeing there.

      Delete
  10. Hi Pedro, I have some Correlation issues in my Jmeter script. I am pretty much new to Load testing & JMeter.I have shared the details through gmail. I would request you to have a look into that.

    ReplyDelete
  11. Thanks for the excellent example. i have used the same expression for my application, but while running the script i'm getting following error through "View Result Tree" listener:

    The anti-forgery token could not be decrypted. If this application is hosted by a Web Farm or cluster, ensure that all machines are running the same version of ASP.NET Web Pages and that the <machineKey> configuration specifies explicit encryption and validation keys. AutoGenerate cannot be used in a cluster.

    and the sample getting failed. Please suggest me, what should i do?

    Regards,
    MAZ

    ReplyDelete
    Replies
    1. All the web.configs of the frontends on the farm need to have the same MachineKey, both for the anti-forgery token and eventually for the Authorization Cookie if you have one. Otherwise you'll need to set sticky-sessions on your load balancer.

      Delete
    2. Hello Pedro Sousa,

      Many thanks for quick reply. I don't have access to web config. i also can share with more information that i'm getting through "Request" tab for the sample in the "view results tree" listener:

      POST data:
      __RequestVerificationToken=%24%7BRVT%7D&CardType=Visa&Cardnumber=4111111111111111&MonthlyCreditCommitments=12000&CardName=Visa+Card&IssueNumber=123456&CardCV2=123&Month=12&Year=20&BankName=Accept&Bankaccountnumber=12345678&Banksortcode=123456&NameOnAccount=Accept

      Cookie Data:
      ASP.NET_SessionId=kbsljd1qgtbmg2dfeaxnwona; __RequestVerificationToken=IGfFTihMgD-uSdwpmWcJD6F1dDalRT38EDVVD-c1vddDm3NQyRamsdUzhG6pG2R_6LpxeyfiR9GnlyJMk17ySQ2

      BR
      MAZ

      Delete
    3. Thanks, i have done successfully.

      BR
      MAZ

      Delete
    4. How did you got it sorted @00000977264360301009 (BR MZ) ?
      I still coudnt got it sorted, can you assist me?

      Delete
  12. This comment has been removed by the author.

    ReplyDelete
  13. Hi Pedro, I have same problem:

    "The anti-forgery token could not be decrypted. If this application is hosted by a Web Farm or cluster, ensure that all machines are running the same version of ASP.NET Web Pages and that the configuration specifies explicit encryption and validation keys. AutoGenerate cannot be used in a cluster."

    This is just trying to test on localhost. My web.config uses forms Auth and names the cookie .MYSITECOOKIE path to "/" protection to "All". I also set the machine key with the validation and decryption key in the web config.
    I think one of these may be causing the error, but I don't know how to setup Jmeter to accomodate that? I tried to set a cookie in the cookie manager, but the value changes every time. So not sure what to do.

    My cookies in my login POST cookies look like this:

    Request Cookies 759
    .BSSIBCASPXROLES flGKtkTwvmWwh21nZBFDXzw8nQwxY_ZrMSySHt2fKrgNhJ8OAXRm0d0SqvbpT2ovsDqeTgBAzHKaFetY6wuncSpIO_4HzygmhXFrh86T5WFxLJqfROLiATY0ehHX4xNTWVEdthbTpTnObtPmW-Dsi2Ck8Zken0LZeB2so9MFD0EuFKoWHtz2NyTmEBhUMJcpu4VxtMkCQ0mWqEPLgt5GLsCZ7aAxSXaeugs9bLfpw8dW2Z8BJNib3WHGNu3ZUDlvpGfC5S9GtRA_vLQzRN3TTce7Y8H_8OJwcINCQs0L8FQpb7puEUWyryPgpOJgsAWu7atTzr_M5aTxUq5ox9EJ-1T5-x4jjrDS8oEwiN488h4KJun0N5zn_zJMyZFPTFnqISF_WMEesJPifiSs_Dc3cOKqjDsCNeUWINgy9Fjcsw51HkbOlK4cUc00I7vMSuR-6-UuLaMI_2qYP_aDn9A4nUYqG3BgqF7AePFC3wyZCeuTLplBEQVpu32bTafKCiN6ITL0ZA45nE7aXBIbZymmNcuUdxw1 N/A N/A N/A 557
    ASP.NET_SessionId unxa14yejhybfu0gtgxydjzc N/A N/A N/A 44
    __RequestVerificationToken QDJuPhySwuYAprk12nvNSARsm7GLY7jhGBwXGLelVz8UVervMQ-o1bzAnYgzZ9bSTkEB37i4IG2lxwUAqTZrwF_vGX81 N/A N/A N/A 121
    Response Cookies 199
    .MYSITECOOKIE 4DA9837B03C490241263BD2A19E6F63731D25BBDA666893E869BAB0D2FAFABF650535CC46A8D5359FA8F3F5706FF50EC153A36548E18532508D35D03A8C6BC09C2C4AE3DB4CC54239BDF2BB8B05DB4A4FD06032E

    Any help you can provide would be appreciated.

    ReplyDelete
  14. excellent tutorial
    it would be useful to provide JMeter test file used in the tutorial for download from this page

    ReplyDelete
  15. Thanks a lot Pedro!!!
    I found this article really useful, I come from Front end automation and I had some troubles trying to understand how performance testing works.

    ReplyDelete
  16. One Question, In the BD I see that has not been entered and JMeter everything goes OK.

    ReplyDelete
  17. need solution for anti forgery case when user trying to login multiple time

    ReplyDelete