{"id":90433,"date":"2021-03-26T15:23:19","date_gmt":"2021-03-26T15:23:19","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=90433"},"modified":"2021-04-27T13:58:11","modified_gmt":"2021-04-27T13:58:11","slug":"load-stress-testing-net-apps-with-apache-jmeter","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/devops\/testing\/load-stress-testing-net-apps-with-apache-jmeter\/","title":{"rendered":"Load\/stress testing .NET apps with Apache JMeter"},"content":{"rendered":"<p>You created a brand-new API developed on the REST principles and set up under the ASP.NET world. Now you want to test it. More specifically, you want to create and run some stress tests for your API in to understand how fast it is and how much load it supports.<\/p>\n<p>The .NET team <a href=\"https:\/\/devblogs.microsoft.com\/devops\/cloud-based-load-testing-service-eol\/\">has discontinued<\/a> their cloud-based load testing service a while ago under Visual Studio 2019 due to the lack of community adoption. Users in the community now had to think about the one million-dollar question: What is the best load\/stress testing tool for my .NET projects?\u00a0The answer starts right before the \u201c.NET projects\u201d part. Most people load testing nowadays are doing that against web APIs or, at least, web applications (that handle endpoints the same way with different media types).<\/p>\n<p>Because of this particular point, these types of tools are mostly agnostic because some of them provide very platform-specific features like the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Scripting_for_the_Java_Platform\">JSR223<\/a> (a scripting language for Java apps) assertions that <a href=\"https:\/\/jmeter.apache.org\/usermanual\/component_reference.html#JSR223_Assertion\">JMeter supplies<\/a>, for example.<\/p>\n<p>In this article, you\u2019re going to dive into the universe of <a href=\"https:\/\/jmeter.apache.org\/\">Apache JMeter<\/a>, one of the most used agnostic load test tools in the software development community by testing it against a REST application created in ASP.NET.<\/p>\n<h2>JMeter setup<\/h2>\n<p>JMeter is made with Java 8+, so it requires the JDK (<em>Java Developers Kit<\/em>) on your machine before using it. Be sure to <a href=\"https:\/\/www.oracle.com\/br\/java\/technologies\/javase\/javase-jdk8-downloads.html\">download<\/a> and install it. The installation for JMeter is a little bit different from the usual installer approach. Instead, you must go to the JMeter <a href=\"https:\/\/jmeter.apache.org\/download_jmeter.cgi\">Download page<\/a> and download the binary zipped file.<\/p>\n<p>Unzip it and navigate to the <em>bin<\/em> folder in which you will find a file called <em>jmeter.bat<\/em>. Double-click it and the window shown in Figure 1 will show up.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"813\" height=\"456\" class=\"wp-image-90434\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2021\/03\/word-image-84.png\" \/><\/p>\n<p><strong>Figure 1.<\/strong> <strong>Starting up Apache JMeter tool<\/strong><\/p>\n<p>The GUI tool is going to load in the background until it pops up as shown in Figure 2.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1204\" height=\"677\" class=\"wp-image-90435\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2021\/03\/word-image-85.png\" \/><\/p>\n<p><strong>Figure 2.<\/strong> <strong>Apache JMeter initial screen<\/strong><\/p>\n<p>Take some time to look at the options and menus displayed on this screen.<\/p>\n<h2>The .NET application<\/h2>\n<p>For simplicity, this example will not create an API application from scratch since it\u2019s not the focus of the article. You can test whatever web application or live API on the web that you have handy. However, to get the feeling of local tests, use a CRUD API I\u2019ve developed before in another article: <a href=\"https:\/\/www.red-gate.com\/simple-talk\/dotnet\/net-development\/creating-asp-net-apps-with-react\/\">Creating ASP.NET Apps with React<\/a>.<\/p>\n<p>You may find the source code <a href=\"https:\/\/github.com\/iamjuliosampaio\/ReactASPNET\/\">here<\/a>. Please, clone it to your machine, <em>cd<\/em> into the <em>\\ReactASPNET\\ReactASPCrud\\ReactASPCrud\\<\/em> folder, and run the following command:<\/p>\n<pre class=\"lang:c# theme:vs2012\">dotnet run<\/pre>\n<p>This command will start up the application at <a href=\"http:\/\/localhost:5000\">http:\/\/localhost:5000<\/a>. Leave it there and get back to the tests.<\/p>\n<h2><a id=\"post-90433-_heading=h.3znysh7\"><\/a>JMeter test plans<\/h2>\n<p>In JMeter, everything starts from a <em>Test Plan<\/em>. That\u2019s the very definition of how JMeter groups things together as a composition of runnable tests. You can have as many test plans as you want for a single project.<\/p>\n<p>JMeter loves groupings. Typically, since you\u2019re dealing with stress testing, you have to deal with loops. Loops are used to check if the given resource can handle all the concurrent load that you\u2019d eventually have in a real-world scenario. That\u2019s why you always start by adding a new <em>Thread Group<\/em> to your Test Plan. Figure 3 demonstrates the menus you may access to add a <em>Thread Group<\/em>.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"849\" height=\"403\" class=\"wp-image-90436\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2021\/03\/word-image-86.png\" \/><\/p>\n<p><strong>Figure 3.<\/strong> <strong>Adding a new Thread Group to your Test Plan<\/strong><\/p>\n<p>You may see a bunch of fields under the <em>Thread Properties<\/em> section. Among all these fields, there are three of them that are more important:<\/p>\n<ul>\n<li><em>Number of threads (users)<\/em>: as the name suggests, this field represents how many users you envision to be using the same endpoint at the same time. Usually, you\u2019re going to add a value way higher than the average of concurrent users you have in order to stress your system.<\/li>\n<li><em>Ramp-up period (seconds)<\/em>: this field considers a ramp-up strategy over time which tells JMeter how long to wait until loading the next user (request).<\/li>\n<li><em>Loop count<\/em>: how many iterations this thread group test may have?<\/li>\n<\/ul>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"864\" height=\"503\" class=\"wp-image-90437\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2021\/03\/word-image-87.png\" \/><\/p>\n<p><strong>Figure 4.<\/strong> <strong>Configuring the Thread Properties of your Thread Group<\/strong><\/p>\n<p>Great! Now that you have your Thread Group set up, you need to determine what this group will iterate over. You can iterate over many different types of requests, such as JDBC, JMS, FTP, etc. However, since you\u2019re dealing with a REST API, the HTTP requests are the next elements to configure.<\/p>\n<p>But first, take a shortcut through a very nice config called <em>HTTP Request Defaults<\/em>. Figure 5 shows how to access this option.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"871\" height=\"637\" class=\"wp-image-90438\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2021\/03\/word-image-88.png\" \/><\/p>\n<p><strong>Figure 5.<\/strong> <strong>Adding a new HTTP Request Defaults<\/strong><\/p>\n<p>This option helps you globally configure HTTP properties that are most commonly used such as the URL root address of your API, HTTP Authentication settings, IP, port, etc. This is a great option when you have to test many API endpoints within the same Thread Group without having to explicitly duplicate the information.<\/p>\n<p>Go ahead and fill in the options as shown in Figure 6.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"859\" height=\"581\" class=\"wp-image-90439\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2021\/03\/word-image-89.png\" \/><\/p>\n<p><strong>Figure 6.<\/strong> <strong>Setting up HTTP Request Defaults options<\/strong><\/p>\n<p>You may also notice that these configs are being stacked on top of each other on the left panel of the JMeter GUI tool. It stacks things in a nested way in order to keep the hierarchy of your settings, so pay attention to that.<\/p>\n<p>Now that you have a default configuration for your HTTP requests, move on to add the first API endpoint to test. Start with the most simple one: the users\u2019 listing at <a href=\"https:\/\/localhost:5000\/api\/users\">https:\/\/localhost:5000\/api\/users<\/a>.<\/p>\n<p>Figure 7 shows how to add a new <em>HTTP Request Sampler<\/em> element.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"679\" height=\"370\" class=\"wp-image-90440\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2021\/03\/word-image-90.png\" \/><\/p>\n<p><strong>Figure 7.<\/strong> <strong>Adding a new HTTP Request to the Thread Group<\/strong><\/p>\n<p>Figure 8 demonstrates how you may fill in the fields. Remember that the <em>Server Name\/IP<\/em> and <em>Port Number<\/em> were already provided within the previous Defaults setting, so there\u2019s no need to set it again.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1197\" height=\"428\" class=\"wp-image-90441\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2021\/03\/word-image-91.png\" \/><\/p>\n<p><strong>Figure 8.<\/strong> <strong>Setting the HTTP Request endpoint details<\/strong><\/p>\n<p>This is basically everything you need as a minimum setup for an HTTP request suite. However, in order for you to check the results of the stress test, you need to ask JMeter to measure the right metrics. JMeter can deal with a bunch of different and useful result listeners. Take a look at the two most used ones.<\/p>\n<p>Figure 9 and 10 shows how to add a new <em>Summary Report<\/em> and a new <em>Graph Results<\/em> listeners, respectively. Go ahead and do that to your Test Plan as well.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"759\" height=\"542\" class=\"wp-image-90442\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2021\/03\/word-image-92.png\" \/><\/p>\n<p><strong>Figure 9.<\/strong> <strong>Adding a new Summary Report to the Test Plan<\/strong><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"826\" height=\"564\" class=\"wp-image-90443\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2021\/03\/word-image-93.png\" \/><\/p>\n<p><strong>Figure 10.<\/strong> <strong>Adding a new Graph Results to the Test Plan<\/strong><\/p>\n<p>There\u2019s no better way to see what these two listeners can rather than run the Test Plan. Before that, make sure to save your current test settings by hitting Ctrl+S on the keyboard. It\u2019ll trigger a new window (Figure 11) for you to select the directory to save the tests as a file with a .jmx extension.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"502\" height=\"365\" class=\"wp-image-90444\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2021\/03\/word-image-94.png\" \/><\/p>\n<p><strong>Figure 11.<\/strong> <strong>Saving the current test suite<\/strong><\/p>\n<p>You can then add this file to your project repository, and other members of your team can load it on their own JMeter tools as well.<\/p>\n<p>Now, click the <em>Start<\/em> button on the top bar or hit the Ctrl+R keyboard shortcut to execute the Test Plan. As the logs start to show up on the terminal window where you started the local API application, you\u2019ll get to see the summary of the execution when you click the <em>Summary Report<\/em> item on the left panel. That\u2019ll display the screen shown in Figure 12.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1256\" height=\"308\" class=\"wp-image-90445\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2021\/03\/word-image-95.png\" \/><\/p>\n<p><strong>Figure 12.<\/strong> <strong>Displaying the Summary Report whilst running the tests<\/strong><\/p>\n<p>That is useful information since you can see not only the number of samples running at the time but also the average of users, the maximum number of samples at any time during the execution, the error rate, and more.<\/p>\n<p>If you open the <em>Graph Results<\/em> item, that may show similar results as in Figure 13.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1626\" height=\"809\" class=\"wp-image-90446\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2021\/03\/word-image.gif\" \/><\/p>\n<p><strong>Figure 13.<\/strong> <strong>Animated results from the Graph Results whilst running the tests<\/strong><\/p>\n<p>This graph not only brings important information about the number of executed samples, but also graphically displays the <em>throughput<\/em> vs the <em>deviation<\/em> of the tests. The higher the throughput, the more the application can deal with heavy loads of requests. The opposite is true for the deviation. In this example, the average was around 650 requests\/minute.<\/p>\n<h2>Extra listeners<\/h2>\n<p>There are a couple more nice listeners to consider when developing your JMeter tests. Take some time to explore them as well. The first one is the <em>View Results Tree<\/em>. You can add it the same way you added the previous ones, via <em>Add &gt; Listener &gt; View Results Tree<\/em>.<\/p>\n<p>Before you rerun the tests, make sure to clean up the previous execution. You can do that by clicking the &#8220;gear and two brooms&#8221; icon on the top bar. Then, rerun the tests. You may see a similar result to the one shown in Figure 14.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1250\" height=\"710\" class=\"wp-image-90447\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2021\/03\/word-image-96.png\" \/><\/p>\n<p><strong>Figure 14.<\/strong> <strong>Displaying the View Results Tree results<\/strong><\/p>\n<p>Here, JMeter is going very granular by displaying each one of the request\u2019s information as well as HTTP headers, body size, response code and message, and many more. This is great when you need to search for a very explicit error in specific scenarios.<\/p>\n<p>Another significant measure can be displayed by the <em>Response Time Graph<\/em> as shown in Figure 15. It works by plotting a graph with two axes: the time vs the number of requests. This graph shows the request\/response throughput through the time execution.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1255\" height=\"710\" class=\"wp-image-90448\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2021\/03\/word-image-97.png\" \/><\/p>\n<p><strong>Figure 15.<\/strong> <strong>Displaying the Response Time Graph results<\/strong><\/p>\n<p>Again, you can add this type of listener via the same steps as before.<\/p>\n<h2>Response assertions<\/h2>\n<p>During the tests, it is common to have scenarios where you\u2019d like to check for a specific HTTP response status or even if the response body attends to a specific condition. For situations like that, JMeter provides you with the <em>Assertions<\/em>. Start with the HTTP response status assertion configuration.<\/p>\n<p>Figure 16 shows how to add a new one to your <em>Thread Group<\/em>.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"617\" height=\"502\" class=\"wp-image-90449\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2021\/03\/word-image-98.png\" \/><\/p>\n<p><strong>Figure 16.<\/strong> <strong>Adding a new Response Assertion checker<\/strong><\/p>\n<p>Make sure to check the <em>Response Code<\/em> option in the <em>Field to Test<\/em> config. The <em>Pattern Matching Results<\/em> must be set as <em>Equals<\/em> as shown in Figure 17.<\/p>\n<p>To add a new pattern click the <em>Add<\/em> button on the bottom side of the screen and add the 200 as the value for the <em>Patterns to Test<\/em> option.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1121\" height=\"758\" class=\"wp-image-90450\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2021\/03\/word-image-99.png\" \/><\/p>\n<p><strong>Figure 17.<\/strong> <strong>Setting up the Response Assertion<\/strong><\/p>\n<p>That\u2019s it! Whenever you receive an HTTP status code other than 200 the <em>View Results Tree<\/em> listener will show the error.<\/p>\n<p>Another interesting assertion available for APIs that make extensive use of JSON is the <em>JSON Assertion<\/em>. To add it, go to the same menu <em>Add &gt; Assertion &gt; JSON Assertion<\/em> and configure it as shown in Figure 18.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1133\" height=\"402\" class=\"wp-image-90451\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2021\/03\/word-image-100.png\" \/><\/p>\n<p><strong>Figure 18.<\/strong> <strong>Setting up the JSON Assertion<\/strong><\/p>\n<p>The <em>Assert JSON Path exists<\/em> field receives an expression to catch a specific value from the JSON response.<\/p>\n<p>Listing 1 shows an example of the users\u2019 list currently returned by the API. Since the data is always randomly generated at start-up, your test would fail. Make use of this to test the failing assertion. The JSON Assertion states that JMeter must check if the <em>name<\/em> property of the first list item is equal to \u201cSusan MacDonald\u201d. Don\u2019t forget to check the <em>Additionally assert value<\/em> checkbox.<\/p>\n<p><strong>Listing 1.<\/strong> <strong>JSON response from the users API<\/strong><\/p>\n<pre class=\"lang:c# theme:vs2012\">[\r\n   {\r\n      \"id\":1,\r\n      \"name\":\"Mary Gay\",\r\n      \"email\":\"mary@yahoo.com\",\r\n      \"document\":\"0562264851\",\r\n      \"phone\":\"+1 888-452-1232\"\r\n   },\r\n   {\r\n      \"id\":2,\r\n      \"name\":\"Neil Strip\",\r\n      \"email\":\"neil@outlook.com\",\r\n      \"document\":\"-654353851\",\r\n      \"phone\":\"+1 888-452-1232\"\r\n   },\r\n   {\r\n      \"id\":3,\r\n      \"name\":\"Jonathan O'Neil\",\r\n      \"email\":\"jonathan@outlook.com\",\r\n      \"document\":\"0424625040\",\r\n      \"phone\":\"+1 888-452-1232\"\r\n   },\r\n   {\r\n      \"id\":4,\r\n      \"name\":\"Joe Spenser\",\r\n      \"email\":\"joe@outlook.com\",\r\n      \"document\":\"1415116364\",\r\n      \"phone\":\"+1 888-452-1232\"\r\n   },\r\n   {\r\n      \"id\":5,\r\n      \"name\":\"Jonathan O'Neil\",\r\n      \"email\":\"jonathan@hotmail.com\",\r\n      \"document\":\"-367589335\",\r\n      \"phone\":\"+1 888-452-1232\"\r\n   }\r\n]<\/pre>\n<p>Figure 19 shows the View Results Tree execution results.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1131\" height=\"752\" class=\"wp-image-90452\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2021\/03\/word-image-101.png\" \/><\/p>\n<p><strong>Figure 19.<\/strong> <strong>Displaying the wrong JSON assertion results<\/strong><\/p>\n<p>As you can see, the assertion failed because the expected value doesn\u2019t match the received one, which is \u201cMary Gay\u201d.<\/p>\n<p>If you update the <em>JSON Assertion<\/em> accordingly, then your tests will pass.<\/p>\n<h2>Load\/stress testing .NET apps with JMeter<\/h2>\n<p>As you\u2019ve seen, JMeter is a powerful tool that comes with many built-in functionalities for various scenarios. Apart from that, JMeter\u2019s community provides you with an enormous amount of <a href=\"https:\/\/jmeter-plugins.org\/\">open-source plugins<\/a> that you can add to give even more flexibility and power to your load tests. One of them that is very useful is the <a href=\"https:\/\/jmeter-plugins.org\/wiki\/JSONFormatter\/\">JSONFormatter<\/a> that helps with beautifying your HTTP JSON responses.<\/p>\n<p>Last but not least, make sure to refer to its <a href=\"https:\/\/jmeter.apache.org\/\">official docs<\/a> for more on what it is capable of doing.<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Since Microsoft discontinued their cloud-based load testing services, developers need a new way to test applications. Julio Sampaio demonstrates how use one solution, JMeter.&hellip;<\/p>\n","protected":false},"author":323407,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[53,143519],"tags":[5134],"coauthors":[93894],"class_list":["post-90433","post","type-post","status-publish","format-standard","hentry","category-featured","category-testing","tag-sql-prompt"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/90433","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/323407"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=90433"}],"version-history":[{"count":2,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/90433\/revisions"}],"predecessor-version":[{"id":90454,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/90433\/revisions\/90454"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=90433"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=90433"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=90433"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=90433"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}