{"id":14653,"date":"2022-01-26T16:35:26","date_gmt":"2022-01-26T16:35:26","guid":{"rendered":"http:\/\/abstracta.us\/blog\/?p=14653"},"modified":"2025-05-05T21:25:28","modified_gmt":"2025-05-05T21:25:28","slug":"developers-friendly-tools-for-continuous-performance-testing","status":"publish","type":"post","link":"https:\/\/abstracta.us\/blog\/software-testing\/developers-friendly-tools-for-continuous-performance-testing\/","title":{"rendered":"Developer\u2019s friendly tools for continuous performance testing"},"content":{"rendered":"\n<p>How many times have we seen a test infrastructure and methodology where the team is not able to get early feedback about the performance of the system they are developing?  Typically, it is expected to treat performance testing as a \u201cwaterfall project\u201d where we, the testers, prepare a test and run it into production right before its deployment. However, there is a better way to do this and it is with <strong>continuous performance testing<\/strong>. When implemented correctly, it gives confidence to the developers and keeps them aware of any substantial degradation in the system\u2019s performance. <\/p>\n\n\n\n<p>As a response to the growing trend in performance testing tools and as our Chief Technology Officer, <a href=\"https:\/\/www.linkedin.com\/in\/rogerabelenda\/\">Roger Abelenda<\/a> further explores in <a href=\"https:\/\/www.linkedin.com\/pulse\/performance-testing-tools-trend-roger-abelenda\/\">this article<\/a>, we have developed an open-source solution we believe can make a huge contribution to the software engineering community. The project&#8217;s name is called \u201c<strong>JMeterDSL<\/strong>\u201d and we want to show you how you can benefit from it.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"What_is_JMeterDSL\"><\/span>What is JMeterDSL?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p><strong>JMeterDSL is a new Java API that harnesses coding benefits for creating and running JMeter tests.<\/strong> Its main purpose is to provide a programmer-friendly API that allows testers and developers to create more readable test plans with a git-friendly format. This is especially recommended for teams that use, or want to start using, load testing in the CI\/CD environment. The application will help them scale the tests in a continuous flow.&nbsp;<\/p>\n\n\n\n<p>For all testers, developers, and tech leaders out there who want to dig deeper, we strongly recommend you to watch this <a href=\"https:\/\/www.youtube.com\/watch?v=5wO9FlhodcM&amp;t=1s\">webinar<\/a>. In it, <a href=\"https:\/\/www.linkedin.com\/in\/sofiapalamarchuk\/\">Sofia Palamarchuk<\/a> and <a href=\"https:\/\/www.linkedin.com\/in\/melissachawla\/\">Melissa Chawla<\/a> discuss the main advantages of this approach and how it helped <a href=\"https:\/\/www.shutterfly.com\/\">Shutterfly<\/a> implement continuous performance testing.<\/p>\n\n\n\n<p>Having JMeter tests as code presents several advantages such as <strong>making test scripts shorter and therefore faster to read, edit and maintain<\/strong>. It makes it easier to modularize the scripts, and has inline documentation available. JMeterDSL also offers a user\u2019s guide that, besides introducing the DSL itself, aims to reduce JMeter\u2019s learning curve by providing best practices, warnings and tips.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"My_first_test_with_JMeterDSL_step_by_step\"><\/span>My first test with JMeterDSL, step by step<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>We will guide you through the creation of a simple test for the <a href=\"http:\/\/opencart.abstracta.us\/\">Opencart<\/a> site, which visits the website and selects a product. By doing so, we will review the main features that you will need when scripting a performance test.&nbsp;<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Setting_up\"><\/span>Setting up<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p>In this example, we will use Maven. You can check out other options for the project\u2019s set up in the <a href=\"https:\/\/abstracta.github.io\/jmeter-java-dsl\/guide\/#setup\">user guide<\/a>.<\/p>\n\n\n\n<p>To start using the JMeterDSL, you only have to create a Maven project and include <a href=\"https:\/\/mvnrepository.com\/artifact\/us.abstracta.jmeter\/jmeter-java-dsl\">the following dependency<\/a> in your pom.xml file.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;dependency>\n    &lt;groupId>us.abstracta.jmeter&lt;\/groupId> \n    &lt;artifactId>jmeter-java-dsl&lt;\/artifactId> \n    &lt;version>0.28&lt;\/version> \n    &lt;scope>test&lt;\/scope> \n &lt;\/dependency> <\/code><\/pre>\n\n\n\n<p>We will also use JUnit 5 as a test library and AssertJ for test plan assertions.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;dependency>\n    &lt;groupId>org.junit.jupiter&lt;\/groupId> \n    &lt;artifactId>junit-jupiter&lt;\/artifactId> \n    &lt;version>RELEASE&lt;\/version> \n    &lt;scope>test&lt;\/scope> \n &lt;\/dependency><\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;dependency>\n    &lt;groupId>org.assertj&lt;\/groupId> \n    &lt;artifactId>assertj-core&lt;\/artifactId> \n    &lt;version>3.21.0&lt;\/version> \n    &lt;scope>test&lt;\/scope> \n &lt;\/dependency> <\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Recording_the_test_flow\"><\/span>Recording the test flow<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p>Typically in JMeter, we record our tests using the JMeter Recorder or a third-party proxy to capture the requests, as this allows us to see more details than JMeter\u2019s Test Script Recorder. In this example we will use fiddler, you can download it <a href=\"https:\/\/www.telerik.com\/fiddler\">here<\/a>.<\/p>\n\n\n\n<p>Once we have our proxy on, we can execute the steps manually in the browser and view the requests in fiddler.<\/p>\n\n\n\n<p>Having deleted all requests made to other hosts, and static resources (.js, .css and image files), this is what the recording looks like:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/lh5.googleusercontent.com\/ehR-NXgyiBHO4NrgqNgUzumuPiV-7SJL480L-B5aBNCtJfHKhBi7BTaZn-dM6TYzpY2XR7LW7AJGCMWfuEg1L_eo3l_woN1jHp9EnaFR5MH1TwXjqA3-AY76Zs6ttYBKkhvZRhKV\" alt=\"\"\/><\/figure>\n\n\n\n<p>The next step is to map these requests to JMeterDSL.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Creating_the_script\"><\/span>Creating the script<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p>Let\u2019s start with a simple script that executes one iteration of one user executing a GET request to the Opencart home page.<\/p>\n\n\n\n<p>Just like in JMeter, the first step is to create a test plan in which we will add a thread group as a parameter. In the thread group, we can define the number of threads (virtual users), iterations, and samplers to be executed. To execute an HTTP request we use the httpSampler method with the desired URL.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code> import static us.abstracta.jmeter.javadsl.JmeterDsl.*;\n \nimport java.io.IOException;\nimport org.junit.jupiter.api.Test;\n \npublic class OpencartTest {\n \n   @Test\n   public void OpencartTest() throws IOException {\n      testPlan(\n         threadGroup(1, 1,\n             httpSampler(\"http:\/\/opencart.abstracta.us\")\n         )\n      ).run();\n   }\n \n}<\/code><\/pre>\n\n\n\n<p>Having done this, we can add an assertion to validate that the 99th percentile of the requests\u2019 response time is less than 5 seconds. In JMeter, this would be impossible without installing the right plugins. (Check more about <a href=\"https:\/\/abstracta.us\/blog\/performance-testing\/3-key-performance-testing-metrics-every-tester-should-know\/\">Understanding Key Performance Testing Metrics<\/a>)<\/p>\n\n\n\n<p>To do this, we need to save the test plan stats into a variable, so then we can use it to get different metrics of the execution. In this case, we will use <strong><em>sampleTimePercentile99,<\/em><\/strong> and the AssertJ method <strong><em>isLessThan<\/em><\/strong>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import static org.assertj.core.api.Assertions.assertThat;\nimport static us.abstracta.jmeter.javadsl.JmeterDsl.*;\n \nimport java.io.IOException;\nimport java.time.Duration;\nimport org.junit.jupiter.api.Test;\nimport us.abstracta.jmeter.javadsl.core.TestPlanStats;\n \npublic class OpencartTest {\n   \n   private final String protocol = \"http:\/\/\";\n   private final String host = \"opencart.abstracta.us\";\n   private final String baseUrl = protocol + host;\n   \n   @Test\n   public void OpencartTest() throws IOException {\n       TestPlanStats stats = testPlan(\n           threadGroup(1, 1,\n               httpSampler(baseUrl)\n           )\n       ).run();\n       assertThat(stats.overall().sampleTimePercentile99()).isLessThan(Duration.ofSeconds(5));\n   }\n \n}<\/code><\/pre>\n\n\n\n<p>Now when we run the test, if the 99th percentile of the response times is higher than or equal to 5 seconds, the test will fail. As you can see we have parameterized the base url and protocol to avoid duplication.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Adding_headers\"><\/span>Adding headers<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p>We have created the first get request, but it\u2019s still missing the headers. To add them we can use the method <strong><em>header<\/em><\/strong> with the correct name and value.<\/p>\n\n\n\n<p>Looking at the fiddler recording we can see all the headers that are sent along with this request.&nbsp;<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/lh6.googleusercontent.com\/8BFOXK7C-bvdW1T_y53MvBslYEyQhrivxLuAtmZAySNT7yP0iz78R_xHEx7-CCOXXqpgprk27YSk-sMJyPJYlYr9eYPG2779sQRRB0BngPFhldflgglxXT6WpLOqClQs3Iynx9c9\" alt=\"\"\/><\/figure>\n\n\n\n<p>To add them to the sampler we use the method<strong><em> header(\u201cname\u201d,\u201dvalue\u201d)<\/em><\/strong>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>httpSampler(baseUrl)\n       .header(\"Host\", host)\n       .header(\"Connection\", \"keep-alive\")\n       .header(\"Upgrade-Insecure-Requests\", \"1\")\n       .header(\"User-Agent\", \"Mozilla\/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/94.0.4606.81 Safari\/537.36\")\n        .header(\"Accept\", \"text\/html,application\/xhtml+xml,application\/xml;q=0.9,image\/avif,image\/webp,image\/apng,*\/*;q=0.8,application\/signed-exchange;v=b3;q=0.9\")\n       .header(\"Accept-Encoding\", \"gzip, deflate\")\n       .header(\"Accept-Language\", \"en-US,en;q=0.9\")<\/code><\/pre>\n\n\n\n<p>Another option here is using constants. We can import HTTPConstants like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code> import org.apache.jmeter.protocol.http.util.HTTPConstants;<\/code><\/pre>\n\n\n\n<p>And now we can use the constants for the headers names. For example, this is how the host header would look like:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code> .header(HTTPConstants.HEADER_HOST,host)<\/code><\/pre>\n\n\n\n<p>Now let\u2019s add the headers for all the recorded requests. Ultimately, the thread group should look like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>threadGroup(1, 1,\n    httpSampler(baseUrl)\n        .header(\"Host\", host)\n        .header(\"Connection\", \"keep-alive\")\n        .header(\"Upgrade-Insecure-Requests\", \"1\")\n        .header(\"User-Agent\", \"Mozilla\/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/94.0.4606.81 Safari\/537.36\")\n         .header(\"Accept\",\"text\/html,application\/xhtml+xml,application\/xml;q=0.9,image\/avif,image\/webp,image\/apng,*\/*;q=0.8,application\/signed-exchange;v=b3;q=0.9\")\n        .header(\"Accept-Encoding\", \"gzip, deflate\")\n        .header(\"Accept-Language\", \"en-US,en;q=0.9\"),\n       httpSampler(baseProductsUrl + productQuery)\n        .header(\"Host\", host)\n        .header(\"Connection\", \"keep-alive\")\n        .header(\"Upgrade-Insecure-Requests\", \"1\")\n        .header(\"User-Agent\", \"Mozilla\/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/94.0.4606.81 Safari\/537.36\")\n         .header(\"Accept\", \"text\/html,application\/xhtml+xml,application\/xml;q=0.9,image\/avif,image\/webp,image\/apng,*\/*;q=0.8,application\/signed-exchange;v=b3;q=0.9\")\n        .header(\"Referer\", baseUrl + \"\/\")\n        .header(\"Accept-Encoding\", \"gzip, deflate\")\n        .header(\"Accept-Language\", \"en-US,en;q=0.9\"),\n       httpSampler(baseProductsUrl + \"\/review\" + productQuery)\n        .header(\"Host\", host)\n        .header(\"Connection\", \"keep-alive\")\n        .header(\"Accept\", \"text\/html, *\/*; q=0.01\")\n        .header(\"User-Agent\", \"Mozilla\/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/94.0.4606.81 Safari\/537.36\")\n        .header(\"X-Requested-With\", \"XMLHttpRequest\")\n        .header(\"Referer\", baseProductsUrl + productQuery)\n        .header(\"Accept-Encoding\", \"gzip, deflate\")\n        .header(\"Accept-Language\", \"en-US,en;q=0.9\")<\/code><\/pre>\n\n\n\n<p>To tidy up the script we can group the last two requests in a <a href=\"https:\/\/jmeter.apache.org\/usermanual\/component_reference.html#Transaction_Controller\">transaction<\/a>, as they correspond to the same action, and add names to each request, so we can identify these transactions\/steps easily at execution time.&nbsp;<\/p>\n\n\n\n<p>As you can see I have also reduced duplicated code by defining these variables:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>String baseProductsUrl = baseUrl + \"\/index.php?route=product\/product\";\nString productQuery = \"&amp;product_id=40\";<\/code><\/pre>\n\n\n\n<p>We can also use a shared httpHeaders element with headers that are repeated in every request. Like in JMeter, this element will affect all elements at the same level, so to achieve a shared httpHeaders we must create it directly in the thread group within the same scope as the rest of the samplers.<\/p>\n\n\n\n<p>This is how the thread group looks now:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>threadGroup(1, 1,\n       httpHeaders()\n               .header(\"Host\", host)\n               .header(\"Connection\", \"keep-alive\")\n               .header(\"Accept-Encoding\", \"gzip, deflate\")\n               .header(\"Accept-Language\", \"en-US,en;q=0.9\")\n               .header(\"User-Agent\", \"Mozilla\/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/94.0.4606.81 Safari\/537.36\"),\n       httpSampler(\"Enter Opencart website\", baseUrl)\n               .header(\"Upgrade-Insecure-Requests\", \"1\")\n               .header(\"Accept\", \"text\/html,application\/xhtml+xml,application\/xml;q=0.9,image\/avif,image\/webp,image\/apng,*\/*;q=0.8,application\/signed-exchange;v=b3;q=0.9\"),\n       transaction(\"Click product\",\n           httpSampler(\"Click product - product\", baseProductsUrl + productQuery)\n                   .header(\"Upgrade-Insecure-Requests\", \"1\")\n                   .header(\"Accept\", \"text\/html,application\/xhtml+xml,application\/xml;q=0.9,image\/avif,image\/webp,image\/apng,*\/*;q=0.8,application\/signed-exchange;v=b3;q=0.9\")\n                   .header(\"Referer\", baseUrl + \"\/\"),\n           httpSampler(\"Click product - review\", baseProductsUrl + \"\/review\" + productQuery)\n                   .header(\"Accept\", \"text\/html, *\/*; q=0.01\")\n                   .header(\"X-Requested-With\", \"XMLHttpRequest\")\n                   .header(\"Referer\", baseProductsUrl + productQuery)\n           )\n       )<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Adding_response_assertions\"><\/span>Adding response assertions<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p>Validations, such as this one, confirm that the server is responding as expected and verifies that the script we are developing is acting as it should. To add <a href=\"https:\/\/abstracta.us\/blog\/performance-testing\/jmeter-response-assertions-know-validate-http-response-request\/\">assertions<\/a> we will use the element <strong><em>responseAssertion<\/em><\/strong>. We can use methods like <strong><em>containsSubstrings, equalsToStrings, containsRegexes and matchesRegexes<\/em><\/strong>. To use one of them in a sampler we must add it as a child like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>httpSampler(\"Enter Opencart website\", baseUrl)\n       .children(\n               responseAssertion().containsSubstrings(\"&lt;title>Your Store&lt;\/title>\")\n       )<\/code><\/pre>\n\n\n\n<p>In the example above, we are asserting the presence of the title within the obtained response, using a portion of the page\u2019s HTML.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Adding_timers\"><\/span>Adding timers<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p>Timers are important to properly mimic a real user&#8217;s behavior. For adding a timer we can use <strong><em>uniformRandomTimer(minimumMillis,maximumMillis)<\/em><\/strong>. This method adds a pause between the selected values before executing the request. If we want the timer to affect only one sampler, we must add it as its child. Just <a href=\"https:\/\/jmeter.apache.org\/usermanual\/test_plan.html#scoping_rules\">like in JMeter<\/a>, if we were to add the timer at the same level as the request, it would affect every request in the thread group.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>httpSampler(\"Enter Opencart website\", baseUrl)\n       .children(\n               uniformRandomTimer(1000, 2000)\n       )<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Extracting_variables_from_the_response\"><\/span>Extracting variables from the response<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p>With the script as it is, we are always selecting the same product (product id) from the web pages in every run. To make this selection dynamic, let\u2019s extract this product id from the previous response.<\/p>\n\n\n\n<p>To do that, we will use<strong><em> regexExtractor(\u201cvariableName\u201d,\u201dregex\u201d)<\/em><\/strong> as a child of the sampler. It works just like <a href=\"https:\/\/jmeter.apache.org\/usermanual\/component_reference.html#Regular_Expression_Extractor\">JMeter\u2019s Regular Expression Extractor<\/a>, so you can also modify <a href=\"https:\/\/github.com\/abstracta\/jmeter-java-dsl\/blob\/master\/jmeter-java-dsl\/src\/main\/java\/us\/abstracta\/jmeter\/javadsl\/core\/postprocessors\/DslRegexExtractor.java\">other options<\/a> like the template, default value, match number, and target field.<\/p>\n\n\n\n<p>We will define a variable named \u201cproduct\u201d. This variable contains the name of the product that we will extract the ID from, and we will use it to create a regular expression.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>String product = \"iPhone\";<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>httpSampler(\"Enter Opencart website\", baseUrl)\n       .children(\n               regexExtractor(\"productId\", \"product_id=(\\\\d*)\\\">\" + product)\n       )<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Using_extracted_variables\"><\/span>Using extracted variables<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p>To use the extracted value we can reference the variable using <strong><em>\u201c${variableName}\u201d<\/em><\/strong>. So, if we want to use the variable productId to select a product, we can embed it as shown below:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>String productQuery = \"&amp;product_id=${productId}\";<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Adding_CSV_files\"><\/span>Adding CSV files<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p>Now, what if we want each user to select a different product? Let\u2019s add a CSV file with 3 different products and modify the thread group to run 3 users.<\/p>\n\n\n\n<p>To add the CSV we use <strong><em>csvDataSet(\u201ccsvFile\u201d)<\/em><\/strong>. We can use the full or relative path from the root of the project.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>csvDataSet(\".\/src\/test\/java\/products.csv\"),\nthreadGroup(3, 1,\n       httpSampler(\"Enter Opencart website\", baseUrl)\n               .children(\n                       responseAssertion().containsSubstrings(\"&lt;title>Your Store&lt;\/title>\"),\n                       regexExtractor(\"productId\", \"product_id=(\\\\d*)\\\">${product}\"),\n                       uniformRandomTimer(1000, 2000)\n               ),\n       transaction(\"Click product\",\n           httpSampler(\"Click product - product\", baseProductsUrl + productQuery),\n           httpSampler(\"Click product review\", baseProductsUrl + \"\/review\" + productQuery)\n       )\n)<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Debugging\"><\/span>Debugging<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p>For debugging you can add the element <strong><em>resultsTreeVisualizer()<\/em><\/strong> in the test plan. If we enable this option, JMeter\u2019s built-in <a href=\"https:\/\/jmeter.apache.org\/usermanual\/component_reference.html#View_Results_Tree\">View Results Tree<\/a> element will be displayed. This will allow us to view the transactions in real-time while running the script, displaying the requests and responses for each sample in addition to collected metrics.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/lh6.googleusercontent.com\/wPRBdRyfkl5KJDsyxQl6JWGhnjzA266sjMGXvPTt2oUT6obXmRGB1oWYf2ZC0ycJFR9szuXLVZRGc1qj-0J8ljsC-A1CB9ahYcOmV4sLlhUbRcQp1WIIhNB4TCrsu9hm_bKxHBIx\" alt=\"\"\/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Reporting\"><\/span>Reporting<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p>You can generate an html report by simply adding the element <strong><em>htmlReporter(\u201creportDirectory\u201d)<\/em><\/strong> where <em>reportDirectory <\/em>has to be an empty directory. You can generate a new directory every time like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>htmlReporter(\"html-report-\" + Instant.now().toString().replace(\":\", \"-\"))<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Final_Thoughts\"><\/span>Final Thoughts<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>We think JMeterDSL is a great tool for anyone, with or without JMeter and or programming experience. It significantly eases creation, execution and maintenance of performance tests. Whenever you are trying to create load tests in a quick fashion, validate your job, and or include them in a CI\/CD pipeline, JMeterDSL will now be an option.<\/p>\n\n\n\n<p><strong>Now is the time to give JMeterDSL a try! <\/strong><a href=\"https:\/\/github.com\/abstracta\/examples\/tree\/master\/JMeterDSL\"><strong>Check it out<\/strong><\/a><strong> and view the <\/strong><a href=\"https:\/\/abstracta.github.io\/jmeter-java-dsl\/guide\/\"><strong>user guide<\/strong><\/a><strong> for more information.<\/strong>&nbsp;<\/p>\n\n\n\n<p><strong>Help us with any feedback and become a contributor<\/strong>. We will take every comment into consideration: bugs, feature requests, adjustments, anything. We are open to co-create JMeterDSL to make it the <strong>best open source dev-friendly tool for performance testing.<\/strong><\/p>\n\n\n\n<p>We are really looking forward to hearing your thoughts and ideas. Please don\u2019t hesitate to leave us a comment in the comment section below.<\/p>\n\n\n\n<p><br><strong>Follow us on <a href=\"https:\/\/www.linkedin.com\/company\/abstracta\/\">Linkedin<\/a>, <a href=\"https:\/\/www.facebook.com\/AbstractaSoftwareTesting\">Facebook<\/a>, <a href=\"https:\/\/twitter.com\/AbstractaUS\">Twitter<\/a>, and <a href=\"https:\/\/www.instagram.com\/we_are_abstracta\/\">Instagram<\/a> to be part of our community!  <\/strong><\/p>\n","protected":false},"excerpt":{"rendered":"<p>How many times have we seen a test infrastructure and methodology where the team is not able to get early feedback about the performance of the system they are developing? Typically, it is expected to treat performance testing as a \u201cwaterfall project\u201d where we, the&#8230;<\/p>\n","protected":false},"author":63,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[32,1,61],"tags":[444,445],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v14.0.2 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Developer\u2019s friendly tools for continuous performance testing | Abstracta<\/title>\n<meta name=\"description\" content=\"Continuous performance testing gives confidence to the developers and keeps them aware of any substantial degradation in the system\u2019s performance.\" \/>\n<meta name=\"robots\" content=\"index, follow\" \/>\n<meta name=\"googlebot\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<meta name=\"bingbot\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/abstracta.us\/blog\/performance-testing\/developers-friendly-tools-for-continuous-performance-testing\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Developer\u2019s friendly tools for continuous performance testing | Abstracta\" \/>\n<meta property=\"og:description\" content=\"Continuous performance testing gives confidence to the developers and keeps them aware of any substantial degradation in the system\u2019s performance.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/abstracta.us\/blog\/performance-testing\/developers-friendly-tools-for-continuous-performance-testing\/\" \/>\n<meta property=\"og:site_name\" content=\"Blog about AI-powered quality engineering for teams building complex software | Abstracta\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/AbstractaQA\/\" \/>\n<meta property=\"article:published_time\" content=\"2022-01-26T16:35:26+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-05-05T21:25:28+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/abstracta.us\/wp-content\/uploads\/2022\/01\/EN-N1.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"600\" \/>\n\t<meta property=\"og:image:height\" content=\"338\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@AbstractaUS\" \/>\n<meta name=\"twitter:site\" content=\"@AbstractaUS\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebSite\",\"@id\":\"https:\/\/abstracta.us\/blog\/#website\",\"url\":\"https:\/\/abstracta.us\/blog\/\",\"name\":\"Blog about AI-powered quality engineering for teams building complex software | Abstracta\",\"description\":\"AI-powered quality engineering\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":\"https:\/\/abstracta.us\/blog\/?s={search_term_string}\",\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"ImageObject\",\"@id\":\"https:\/\/abstracta.us\/blog\/performance-testing\/developers-friendly-tools-for-continuous-performance-testing\/#primaryimage\",\"inLanguage\":\"en-US\",\"url\":\"https:\/\/lh5.googleusercontent.com\/ehR-NXgyiBHO4NrgqNgUzumuPiV-7SJL480L-B5aBNCtJfHKhBi7BTaZn-dM6TYzpY2XR7LW7AJGCMWfuEg1L_eo3l_woN1jHp9EnaFR5MH1TwXjqA3-AY76Zs6ttYBKkhvZRhKV\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/abstracta.us\/blog\/performance-testing\/developers-friendly-tools-for-continuous-performance-testing\/#webpage\",\"url\":\"https:\/\/abstracta.us\/blog\/performance-testing\/developers-friendly-tools-for-continuous-performance-testing\/\",\"name\":\"Developer\\u2019s friendly tools for continuous performance testing | Abstracta\",\"isPartOf\":{\"@id\":\"https:\/\/abstracta.us\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/abstracta.us\/blog\/performance-testing\/developers-friendly-tools-for-continuous-performance-testing\/#primaryimage\"},\"datePublished\":\"2022-01-26T16:35:26+00:00\",\"dateModified\":\"2025-05-05T21:25:28+00:00\",\"author\":{\"@id\":\"https:\/\/abstracta.us\/blog\/#\/schema\/person\/ebc2b0a576acabf96b4bc0195999617f\"},\"description\":\"Continuous performance testing gives confidence to the developers and keeps them aware of any substantial degradation in the system\\u2019s performance.\",\"breadcrumb\":{\"@id\":\"https:\/\/abstracta.us\/blog\/performance-testing\/developers-friendly-tools-for-continuous-performance-testing\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/abstracta.us\/blog\/performance-testing\/developers-friendly-tools-for-continuous-performance-testing\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/abstracta.us\/blog\/performance-testing\/developers-friendly-tools-for-continuous-performance-testing\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"item\":{\"@type\":\"WebPage\",\"@id\":\"https:\/\/abstracta.us\/blog\/\",\"url\":\"https:\/\/abstracta.us\/blog\/\",\"name\":\"Home\"}},{\"@type\":\"ListItem\",\"position\":2,\"item\":{\"@type\":\"WebPage\",\"@id\":\"https:\/\/abstracta.us\/blog\/performance-testing\/\",\"url\":\"https:\/\/abstracta.us\/blog\/performance-testing\/\",\"name\":\"Performance Testing\"}},{\"@type\":\"ListItem\",\"position\":3,\"item\":{\"@type\":\"WebPage\",\"@id\":\"https:\/\/abstracta.us\/blog\/performance-testing\/developers-friendly-tools-for-continuous-performance-testing\/\",\"url\":\"https:\/\/abstracta.us\/blog\/performance-testing\/developers-friendly-tools-for-continuous-performance-testing\/\",\"name\":\"Developer\\u2019s friendly tools for continuous performance testing\"}}]},{\"@type\":[\"Person\"],\"@id\":\"https:\/\/abstracta.us\/blog\/#\/schema\/person\/ebc2b0a576acabf96b4bc0195999617f\",\"name\":\"Belen Vignolo\",\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\/\/abstracta.us\/blog\/#personlogo\",\"inLanguage\":\"en-US\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/cd14ff30194e04d370ff801dd1c0f3cb?s=96&d=blank&r=g\",\"caption\":\"Belen Vignolo\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","_links":{"self":[{"href":"https:\/\/abstracta.us\/blog\/wp-json\/wp\/v2\/posts\/14653"}],"collection":[{"href":"https:\/\/abstracta.us\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/abstracta.us\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/abstracta.us\/blog\/wp-json\/wp\/v2\/users\/63"}],"replies":[{"embeddable":true,"href":"https:\/\/abstracta.us\/blog\/wp-json\/wp\/v2\/comments?post=14653"}],"version-history":[{"count":14,"href":"https:\/\/abstracta.us\/blog\/wp-json\/wp\/v2\/posts\/14653\/revisions"}],"predecessor-version":[{"id":14723,"href":"https:\/\/abstracta.us\/blog\/wp-json\/wp\/v2\/posts\/14653\/revisions\/14723"}],"wp:attachment":[{"href":"https:\/\/abstracta.us\/blog\/wp-json\/wp\/v2\/media?parent=14653"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/abstracta.us\/blog\/wp-json\/wp\/v2\/categories?post=14653"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/abstracta.us\/blog\/wp-json\/wp\/v2\/tags?post=14653"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}