Performance Testing with K6

The problem is not that testing is the bottleneck. The problem is that you don’t know what’s in the bottle. That’s a problem that testing addresses.

— Michael Bolton, author, “Rapid Software Testing.”

You won’t find a lot of engineers out there who say they get excited about testing. Quality assurance (QA) is the kind of unglamorous chore that developers often approach with reluctance—like leg day at the gym. But forgive me if I wax enthusiastic about k6, the performance testing package that is the latest addition to my tool belt. K6 is a modern open-source testing tool written in Go. However, it provides JavaScript libraries to write test cases, so engineers and testers can use the k6 libraries to test their APIs and applications.

In this blog post, I explore what I have learned from my brief rendezvous with k6, which is said to make load testing as easy as unit testing. And I am pleased to report that I am thoroughly enjoying it! Here I’ll describe my experience with the tool as part of an application modernization project, detailing what I learned and the steps I took along the way.


In the past couple of quarters, my team was working on a project to decompose a legacy monolith service. Our goal was to replace it by developing new micro/mini RESTFul web services. Because of the day-to-day support overhead, performance issues, and difficulties delivering any change in production quickly, we made a strategic decision to split the legacy project. One MySQL database was supporting the three different services and multiple MapReduce processing jobs, creating a single point of failure for several products.

My team and I started with the smallest and independent API resources to disintegrate them from the monolith. After thoroughly reviewing all the pieces, we started working on our schema design and made necessary changes in the internal APIs design while making sure that all the features of our public APIs remained passive. Soon our new service was ready.

The main challenges to address ahead of deployment were:

  • Conduct passivity check
  • Improve performance and performance measurement
  • Reduce maintenance and support overhead
  • Estimate resource utilization for cost optimization
  • Plan release to production with little to no downtime

Why K6?

“Write a load test like a unit test.” It’s a phrase from k6’s web site that immediately grabbed my attention when I explored the tool after a colleague introduced me to it. I started reading the documentation and the first thing that I liked was that I can use JavaScript to write a performance test. It was a winning moment for me being a Java developer—I already knew how to write JavaScript.

I used Home Brew to install the k6 libraries on my MacBook and was ready to roll in less than a minute. (Figure 1)

Figure 1. Home Brew Install

As a Java developer, the learning curve is gentle, and I found the k6 documentation both robust and well-written. I was able to bang out my first unit test in just a few minutes and to later enhance the code to load test the service. It was love at first sight. Even better, k6 isn’t a browser-based utility—I could run the test on a command-line tool with a simple one-line command.

I have used other testing tools like Apache Benchmark, Jmeter, Gatling, and Postman, but k6 outshines them all with its ease of writing workflows using various operations provided by a service. Adding field validations, asserting outputs, generating dynamic dataset, and debugging the tests were all likewise very easy compared to other tools I have used in the past.

My First K6 Test

I wrote two JavaScript functions and my first load test was ready, like so:

Figure 2. First Load Test

Note that I skipped other parts of my test to focus on the important details and show the ease of writing a test.

Now to run the test, I used k6 options to call a GET /Consumers API and run 10 iterations for 10 virtual users. I used the check function provided by the k6 library to assert the response HTTP code. For more available options, please refer to k6 Options.

Figure 3. Running the Test

The summary provided by k6 is self-explanatory, we can see how many requests k6 generated, the success request rate, and many other important metrics. The result summary looks like this (Figure 4):

Figure 4. The Result Summary

For more details about each metric please check Metrics. This resource also manifests many other output metrics based on your test configuration.

I used k6 and its features to work through with the challenges mentioned above. Over time I was able to increase the level and complexity of testing, as follows:

  • For the passivity check, I asserted the API response under various load conditions and by using all known workflows.
  • I have used k6 while local, dev, and QA environment to perform unit and load testing. After running the performance test using k6, I used NewRelic to compare the performance of legacy and new services by feeding the service logs to it. I presented the before and after performance comparison to all the stakeholders using the service.
  • Because I can dynamically generate the test data using JavaScript functions and classes, I used the load test to create production-level traffic in our staging environment during the deployment of newer artifacts. That not only helped me to find out if we will face challenges during our product delivery, but it also let me better plan the delivery schedule. We were able to monitor system performance and avoid last-minute deployment-time surprises. I used Github to source control my testing code.
  • I ran a k6 test with a heavier load than our production traffic to identify the resources that we will need to run the service effectively and efficiently. I used the Grafana dashboard to generate a report of resource utilization and cost to run the service.
  • I documented the timelines needed to roll out the changes to production by running various test scenarios. This helped me successfully communicate the plan to all the stakeholders.
  • After production deployment and before enabling the service to end-users, I was able to run my test in production without affecting other businesses. This allowed me to test the correctness of the software and clean up the test data at the end of the testing cycle.

K6 integrates with many test visualization tools, though I have yet to explore them as my k6 journey has just started. In fact, that’s my biggest complaint. Unlike Gatling, k6 lacks a graphical result summary that does not require a third-party integration.


Overall, k6 has helped me up my game in performance testing—it turns out I had nothing to worry about! I was able to pick up the following pro tips, which helped me during my journey:

  • Use environment variables to make your test dynamically accept Dev/Staging/Prod Configs, if there are any.
  • If you want to run long-running tests, do not forget to use the Duration option by using an environment variable (K6_DURATION) or CLI param (–duration/ -d) or programmatically in your JavaScript file.
  • If k6 cannot reach out to your API running behind a proxy server, export the HTTP_PROXY option before running the test, as shown in Figure 5:

Figure 5. Exporting the HTTP_Proxy Option

Finally, I learned there is nothing to stop me, as a developer, from adopting testing tools that can speed me up at the same time they help me deliver higher quality results.

References: k6: Load Testing for Engineering Teams

About The Author

Shradha Khard is an Associate Lead Software Engineer at Cerner Corporation. An industry leader cloud evangelist who is customer obsessed, product focused, and detail oriented, Khard is passionate about workplace equality and women’s growth in the IT sector. Her most precious possessions are her two daughters.