{"id":15350,"date":"2024-03-22T00:00:00","date_gmt":"2024-03-22T07:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/ise\/?p=15350"},"modified":"2024-07-18T11:59:20","modified_gmt":"2024-07-18T18:59:20","slug":"apis-load-testing-using-k6","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/ise\/apis-load-testing-using-k6\/","title":{"rendered":"APIs load testing using K6"},"content":{"rendered":"<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/ise\/wp-content\/uploads\/sites\/55\/2024\/03\/load-testing.png\" alt=\"load-testing\" \/><\/p>\n<h2>Context<\/h2>\n<p>After completing the initial phases of the API endpoints implementation, we needed to plan how we will test the performance, identify potential bottlenecks, and ensure that the system meets the Service Level Objectives (SLOs) requirements. Load testing is used for this purpose.<\/p>\n<p>Load testing is a type of performance testing that tests how the system behaves under varying load conditions, including the impact on responsiveness, throughput, and resource utilization.<\/p>\n<p>In a recent project, we evaluated different tools and decided to use <a href=\"https:\/\/k6.io\/\">K6<\/a> to conduct load testing. This post will go through the load testing concepts and shows how to use the K6 tool to perform load testing for API endpoints.<\/p>\n<p>In this context, we have developed mock Customer API service using Spring Boot. This setup was used to illustrate the process of conducting load-testing with <a href=\"https:\/\/code.visualstudio.com\/\">VS Code<\/a> and <a href=\"https:\/\/k6.io\/\">K6<\/a> which you can find in this <a href=\"https:\/\/github.com\/ISE-Neutrino\/api-testing\">repository<\/a>.<\/p>\n<h2>Load testing<\/h2>\n<p><strong>Why we need load testing:<\/strong><\/p>\n<ul>\n<li>Validate reliability under expected traffic;<\/li>\n<li>Discover bottlenecks and system limits under unusual traffic.<\/li>\n<\/ul>\n<p><strong>When you design your API tests, first you need to answer the following questions:<\/strong><\/p>\n<ul>\n<li>What flows or components do you want to test?<\/li>\n<li>How will you run the test?<\/li>\n<li>What criteria determine acceptable performance?<\/li>\n<\/ul>\n<p><strong>The test goal determines the test type:<\/strong><\/p>\n<ul>\n<li><strong>Smoke test:<\/strong> Verify the system functions with minimal load.<\/li>\n<li><strong>\u201cAverage\u201d load test:<\/strong> Discover how the system functions with typical traffic.<\/li>\n<li><strong>Stress test:<\/strong> Discover how the system functions with the load of peak traffic.<\/li>\n<li><strong>Spike test:<\/strong> Discover how the system functions with sudden and massive increases in traffic.<\/li>\n<li><strong>Breakpoint test:<\/strong> Progressively ramp traffic to discover system breaking points.<\/li>\n<li><strong>Soak test:<\/strong> Discover whether or when the system degrades under loads of longer duration.<\/li>\n<\/ul>\n<p><strong>The testing strategy likely follows something like this procedure:<\/strong><\/p>\n<ul>\n<li><strong>Script the test:<\/strong> Write user flows, parameterize test data, and group URLs.<\/li>\n<li><strong>Assert performance and correctness:<\/strong> Use Checks to assert system responses and use Thresholds to ensure that the system performs within your SLOs.<\/li>\n<li><strong>Model and generate load:<\/strong> Model the workload that&#8217;s appropriate to your test goals.<\/li>\n<li><strong>Iterate over your test suite:<\/strong> Over time, you&#8217;ll be able to reuse script logic and run tests with a wider scope or as a part of your automated testing suite.<\/li>\n<\/ul>\n<h2>K6<\/h2>\n<p><strong>K6<\/strong> is an open-source load testing tool that makes performance testing easy and productive for engineering teams. It is Developer and Source control friendly and has a small learning curve(Javascript). You can run k6 locally, in the cloud, or as part of your CI\/CD pipeline.<\/p>\n<p><strong>Key Features at a Glance:<\/strong><\/p>\n<ul>\n<li><strong>Test types:<\/strong> Primarily designed for supporting performance testing types such as load, stress, spike, and more.<\/li>\n<li><strong>Target Users:<\/strong> Well-suited for developers and performance engineers seeking a user-friendly, modern tool for effective performance testing.<\/li>\n<li><strong>Technology Stack:<\/strong> Uses JavaScript and ES6 for scripting, making it accessible to developers familiar with these technologies.<\/li>\n<li><strong>Learning Curve:<\/strong> With a relatively low learning curve, K6 is particularly friendly to developers with JavaScript experience.<\/li>\n<li><strong>Scalability:<\/strong> Designed for horizontal scaling, allowing you to easily distribute load tests across multiple machines.<\/li>\n<li><strong>Support:<\/strong> Offers community support and a commercial version (k6 Cloud) with additional features and support options.<\/li>\n<li><strong>Test Plans Source Control:<\/strong> Supports source control and can be integrated with version control systems like Git for managing test scripts.<\/li>\n<li><strong>Metrics:<\/strong> Provides essential performance metrics out of the box, including response times, requests per second, and errors.<\/li>\n<li><strong>Reporting :<\/strong> Provides basic reporting capabilities, which can be further enhanced with plugins and integrations.<\/li>\n<li><strong>Thresholds:<\/strong> Enables setting of performance thresholds based on your SLO requirements.<\/li>\n<li><strong>Automation CLI:<\/strong> Comes with a CLI for automation, simplifying the process of running tests from the command line or integrating with automation scripts.<\/li>\n<li><strong>Cloud Native:<\/strong> Container-friendly, making it suitable for running in cloud environments and container orchestration platforms.<\/li>\n<li><strong>Supported Protocols:<\/strong> Offers support to a range of protocols, including HTTP, WebSocket, gRPC, and more, through community-developed extensions.<\/li>\n<li><strong>Observability Integration:<\/strong> Facilitates the integration of test results with Observability systems such as AppInsights, Datadog, Grafana, and more.<\/li>\n<li><strong>Cost:<\/strong> Offers an open-source version and a commercial version (k6 Cloud) with additional features and support, priced based on usage.<\/li>\n<\/ul>\n<h2>K6 Installation<\/h2>\n<p>K6 can be installed on Windows, Linux, and macOS. It can also be installed as a Docker image, or you can use the hosted version (k6 Cloud).<\/p>\n<p>In this article, we will install K6 on Linux. For more information about installation on other platforms, please refer to the <a href=\"https:\/\/grafana.com\/docs\/k6\/latest\/get-started\/installation\/\">K6 documentation<\/a>.<\/p>\n<pre><code class=\"language-bash\"># Install K6 on Linux\r\n# https:\/\/grafana.com\/docs\/k6\/latest\/get-started\/installation\/#linux\r\nsudo gpg -k\r\nsudo gpg --no-default-keyring --keyring \/usr\/share\/keyrings\/k6-archive-keyring.gpg --keyserver hkp:\/\/keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69\r\necho \"deb [signed-by=\/usr\/share\/keyrings\/k6-archive-keyring.gpg] https:\/\/dl.k6.io\/deb stable main\" | sudo tee \/etc\/apt\/sources.list.d\/k6.list\r\nsudo apt-get update\r\nsudo apt-get install k6<\/code><\/pre>\n<h2>Create test suite<\/h2>\n<p>K6&#8217;s test suite are written in JavaScript, making it easy to get started with performance testing. You can use the K6 CLI to run tests from the command line or integrate them with your automation scripts.<\/p>\n<p>Each test scenario consists of three sections:<\/p>\n<ul>\n<li>Options (Configuration)<\/li>\n<li>Setup (Initializations)<\/li>\n<li>Test Cases<\/li>\n<\/ul>\n<h3>1. Test Options<\/h3>\n<p>The <code>Test Options<\/code> section defines test-run behavior, including the following:<\/p>\n<ul>\n<li><strong>Tags:<\/strong> Defines the tags to be added to the test metrics and results, which can later be used to filter results<\/li>\n<li><strong>Thresholds:<\/strong> Defines the performance Thresholds, used to ensure that the API performs within your SLOs<\/li>\n<li><strong>Scenarios:<\/strong> Defines the test scenarios, each scenario defining the function to be called with the test options, such as:\n<ul>\n<li>Executor type: Constant or Ramping Arrival Rate, Constant or Ramping VUs (virtual users), and <a href=\"https:\/\/k6.io\/docs\/using-k6\/scenarios\/executors\/\">more<\/a>;<\/li>\n<li>VUs: The number of virtual users or workers<\/li>\n<li>Duration of the test<\/li>\n<li>Rate: The number of iterations per time unit<\/li>\n<li>Time unit: The time unit for the rate<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><em>More information about the test options can be found <a href=\"https:\/\/k6.io\/docs\/using-k6\/k6-options\/reference\/\">here<\/a><\/em>.<\/p>\n<p>Following is an example of the Test Options section for the addCustomer endpoint test case.<\/p>\n<pre><code class=\"language-javascript\">\/**\r\n * Test configuration\r\n *\/\r\nexport const options = {\r\n    tags: {\r\n        test: 'customers-service',\r\n        test_run_id: `Customers-Service-Load-${__ENV.K6_ENV}`,\r\n    },\r\n    thresholds: {\r\n        \/\/ addCustomer thresholds\r\n        'http_req_failed{test_type:addCustomer}': ['rate&lt;0.01'], \/\/ http errors should be less than 1%, availability\r\n        'http_req_duration{test_type:addCustomer}': ['p(95)&lt;200'], \/\/ 95% of requests should be below 200ms, latency\r\n    },\r\n    scenarios: {\r\n        \/\/ Load testing using K6 constant-rate scenario\r\n        addCustomer_constant: {\r\n            executor: 'constant-arrival-rate',\r\n            rate: 10000, \/\/ number of iterations per time unit\r\n            timeUnit: '1m', \/\/ iterations will be per minute\r\n            duration: '1m', \/\/ total duration that the test will run for\r\n            preAllocatedVUs: 2, \/\/ the size of the VU (i.e. worker) pool for this scenario\r\n            maxVUs: 50, \/\/ if the preAllocatedVUs are not enough, we can initialize more\r\n            tags: { test_type: 'addCustomer' }, \/\/ different extra metric tags for this scenario\r\n            exec: 'addCustomer',\/\/ Test scenario function to call\r\n        }\r\n    }\r\n};<\/code><\/pre>\n<h3>2. Setup<\/h3>\n<p>The <code>Setup<\/code> section is optional and is used to set up the test environment, such as acquiring the authentication token and preparing the test initialization data.<\/p>\n<pre><code class=\"language-javascript\">\r\n\/**\r\n * prepare the test data like authentication\r\n * @returns Initial data for each test case\r\n *\/\r\nexport function setup() {\r\n    \/\/ Get the authentication token\r\n    const token = getAuthToken();\r\n    return { access_token: token };\r\n}\r\n<\/code><\/pre>\n<h3>3. Test Scenario<\/h3>\n<p>This section defines the actual testing logic, which could be one or more test scenarios and each scenario could test a single endpoint or a complete flow, It uses the K6 modules such as:<\/p>\n<ul>\n<li>HTTP model to send HTTP requests to the API endpoints;<\/li>\n<li>Check and assertions modules to validate the response;<\/li>\n<li>More modules can be found <a href=\"https:\/\/k6.io\/docs\/using-k6\/\">here<\/a>.<\/li>\n<\/ul>\n<pre><code class=\"language-javascript\">\/**\r\n * Add customer test case\r\n *\/\r\nexport function addCustomer(data) {\r\n\r\n    const url = __ENV.CUSTOMERS_API_URL + \"\/\";\r\n    const headers = {\r\n        'Authorization': `Bearer ${data.access_token}`,\r\n        'Content-Type': 'application\/json'\r\n    };\r\n\r\n    const payload = `{\r\n        \"name\": \"Test Customer\",\r\n        \"email\": \"email@test.com\"\r\n    }`;\r\n\r\n    var response = http.post(url, payload, { headers: headers });\r\n\r\n    check(response, { 'status is 200': (r) =&gt; r.status === 200 });\r\n    if (response.status != 200) {\r\n        console.log(`operation: addCustomer, url: ${url}, Status:${response.status}`);\r\n    }\r\n}<\/code><\/pre>\n<h2>Execute test script<\/h2>\n<p>After creating the test script, we can execute it using the K6 CLI, which allows you to run single or multiple test files, and pass environment variables to the test execution.<\/p>\n<pre><code class=\"language-bash\"># Execute load test using k6 CLI\r\nk6 run -e K6_ENV=local -e CUSTOMERS_API_URL=${CUSTOMERS_API_URL} customers-service.js<\/code><\/pre>\n<p>The sample repository contains <code>make commands<\/code> as shorthand to execute the test cases.<\/p>\n<pre><code class=\"language-bash\"># Execute load test\r\nmake load-test<\/code><\/pre>\n<h2>Test Results<\/h2>\n<p>When the test is complete, k6 prints a top-level summary of the aggregated results to stdout. In addition to this default summary, k6 can output results in other formats including JSON, CSV, and more.<\/p>\n<p>The summary report of the test script includes:<\/p>\n<ul>\n<li>Summary statistics about each built-in and custom metric (e.g. mean, median, p95, etc)<\/li>\n<li>A list of the test&#8217;s groups and scenarios<\/li>\n<li>The pass\/fail results of the test&#8217;s thresholds and checks<\/li>\n<\/ul>\n<p><em>The follow image shows the test results for the addCustomer endpoint.<\/em><\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/ise\/wp-content\/uploads\/sites\/55\/2024\/03\/load-testing-result.png\" alt=\"load-testing\" \/><\/p>\n<h3>K6 Results Metrics<\/h3>\n<p>Every k6 test emits <a href=\"https:\/\/k6.io\/docs\/using-k6\/metrics\/reference\/\">built-in metrics<\/a>. I will spotlight the most important HTTP metrics that can help measure the performance of the system and start to setup thresholds against the expected SLOs.<\/p>\n<ul>\n<li><strong>http_reqs:<\/strong> How many total HTTP requests k6 generated, (system throughput);<\/li>\n<li><strong>http_req_duration:<\/strong> Total time for the request to be completed. It&#8217;s equal to http_req_sending + http_req_waiting + http_req_receiving (system latency);<\/li>\n<li><strong>http_req_failed:<\/strong> The number of failed HTTP requests (system availability).<\/li>\n<\/ul>\n<p><em>More information about metrics can be found <a href=\"https:\/\/k6.io\/docs\/using-k6\/metrics\/reference\/\">here<\/a>.<\/em><\/p>\n<h2>Conclusion<\/h2>\n<p>In this post, we&#8217;ve demonstrated how to use the K6 framework to perform load testing for API endpoints, covering the selection criteria for choosing K6 as a load-testing tool as well as how to install K6 on Linux. We&#8217;ve shown how to easily build a load test case using JavaScript and how to execute the test case using the K6 CLI. We have also shared the test results and the different K6 metrics that can be used to measure the performance of the system.<\/p>\n<p>K6 has proven to be a very powerful tool for load testing. It is easy to use and has a lot of features that can be used to build a comprehensive load test suite and can be seamlessly integrated into the development loop and CI\/CD pipelines.<\/p>\n<h2>References<\/h2>\n<ul>\n<li><a href=\"https:\/\/microsoft.github.io\/code-with-engineering-playbook\/automated-testing\/performance-testing\/\">ISE performance testing<\/a><\/li>\n<li><a href=\"https:\/\/k6.io\/docs\/testing-guides\/api-load-testing\">API Load Testing<\/a><\/li>\n<li><a href=\"https:\/\/k6.io\/docs\/test-types\/load-test-types\">Load test types<\/a><\/li>\n<li><a href=\"https:\/\/k6.io\/docs\/testing-guides\/automated-performance-testing\/\">Automated performance testing<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>In this post, We will go through the load testing concepts and how to use the K6 framework to perform load testing for API endpoints.<\/p>\n","protected":false},"author":119131,"featured_media":15351,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1,3451],"tags":[3398,3472,3504,3503,3430,3346,3384],"class_list":["post-15350","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-cse","category-ise","tag-api","tag-http","tag-k6","tag-load","tag-performance","tag-testing","tag-vscode"],"acf":[],"blog_post_summary":"<p>In this post, We will go through the load testing concepts and how to use the K6 framework to perform load testing for API endpoints.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts\/15350","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/users\/119131"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/comments?post=15350"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts\/15350\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/media\/15351"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/media?parent=15350"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/categories?post=15350"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/tags?post=15350"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}