Talking About TestingATT</>
Performance Testing with k6

Score 100% on the quiz to continue

Lesson
Free Preview

Your first k6 script

Write a script with the default function, http.get(), and sleep(), run it, and read the summary metrics.

A k6 script is a JavaScript file. That is the whole trick, and it is why a tester who already writes Cypress or Playwright tests can be productive with k6 in an afternoon.

What changes is not the language. What changes is that k6 takes your script and runs it over and over, in many copies at once, and measures every HTTP request it makes along the way.

The smallest script that does something

Create a file at k6/first-test.js with this content:

import http from 'k6/http'
import { sleep } from 'k6'

export default function () {
  http.get('http://localhost:3000/products')
  sleep(1)
}

That is a complete, valid performance test. Three things are worth naming in it.

The default function

The function exported as default is your test body. k6 calls it once, then calls it again, then again, for as long as the test is configured to run. One pass through it is called an iteration.

This is the biggest mental shift coming from functional testing. In Cypress, your test runs once and then it is over. In k6, your test is a loop body that will be executed thousands of times by hundreds of concurrent virtual users, so anything you put in it happens thousands of times.

👨‍🏫 That is also why a k6 script has no it() or test() blocks. There is nothing to name, because the same code runs again and again. The unit of organisation in k6 is the scenario, not the test case.

http.get()

The k6/http module is how you make requests. http.get(url) sends a GET and returns a response object with the fields you would expect:

const response = http.get('http://localhost:3000/products')

console.log(response.status)         // 200
console.log(response.timings.duration) // how long it took, in milliseconds
console.log(response.json('total'))  // reads a field out of the JSON body

Every request made through this module is automatically measured and folded into the metrics k6 prints at the end. You do not instrument anything yourself.

sleep()

sleep(1) pauses the virtual user for one second before it starts the next iteration.

It is tempting to delete it and "test harder". Do not. A virtual user with no sleep() is not a user, it is a tight loop, and it will generate a request pattern no real population of humans would ever produce. We will treat this properly in the lesson on real user flows, where sleep() gets its real name: think time.

Running it

k6 run k6/first-test.js

By default, k6 runs one virtual user for one iteration and exits. That is deliberate: your first run should tell you whether the script works, not how fast the API is.

Reading the summary

When the run finishes, k6 prints a block of metrics. Trimmed down to what matters today:

     checks.........................: 0.00%  0 out of 0
     data_received..................: 12 kB  11 kB/s
     data_sent......................: 391 B  366 B/s
     http_req_duration..............: avg=8.42ms  min=8.42ms med=8.42ms max=8.42ms p(90)=8.42ms p(95)=8.42ms
     http_req_failed................: 0.00%  0 out of 1
     http_reqs......................: 1      0.93/s
     iteration_duration.............: avg=1.01s   min=1.01s  med=1.01s  max=1.01s  p(90)=1.01s  p(95)=1.01s
     iterations.....................: 1      0.93/s
     vus............................: 1      min=1        max=1

Four of those lines carry almost all of the meaning:

  • http_req_duration is how long the requests took. This is the number people mean when they say "response time".
  • http_req_failed is the error rate. 0.00% here, and it is the first thing you should look at, because a fast API that is returning errors is not fast, it is broken.
  • http_reqs is how many requests were made, and the rate per second next to it is your throughput.
  • iterations is how many times your default function ran.

Notice that avg, min, med, max, and the percentiles are all the same number. With a single request there is only one measurement, so every statistic describes it. That changes the moment you add a second virtual user, and reading those columns properly is what lesson Reading the results is entirely about.

👨‍🏫 Also notice iteration_duration is about one second longer than http_req_duration. That is your sleep(1). Think time counts towards the iteration, not towards the request.

Commands overview 📖

k6 run

Runs a k6 script. With no options, it executes one iteration with one virtual user.

Syntax:

k6 run <script-path>

Example:

k6 run k6/first-test.js

k6 run --vus --duration

Overrides the number of virtual users and how long the test runs, straight from the command line.

Syntax:

k6 run --vus <number> --duration <time> <script-path>

Example:

k6 run --vus 10 --duration 30s k6/first-test.js

Suggested content 📚

Exercise 🎯

Write k6/first-test.js yourself, run it, and then change one thing at a time to see what each change does to the summary:

  1. Run it as it is, with the default single iteration.
  2. Run it again with --vus 5 --duration 10s and compare http_reqs and iterations.
  3. Remove the sleep(1), run the ten-second version again, and look at what happened to http_reqs and to http_req_duration.
  4. Put the sleep(1) back.
🙊 In step 3 you should see http_reqs jump by roughly an order of magnitude, because five virtual users with no think time hammer the API as fast as it can answer. Watch http_req_duration climb at the same time: you did not make the API slower, you made the load heavier. This is the first time you will have caused a performance change on purpose, and it is worth sitting with for a minute.

The finished version of this script is in the k6/ directory of the course repository.

Show the world what you learned 🌎

To show your professional network what you learned in this lesson, post the following on LinkedIn.

I am taking the "Performance Testing with k6" course by @Walmyr Lima e Silva Filho at the @Talking About Testing School, where I wrote my first k6 script and learned to read the summary metrics it prints: response time, error rate, throughput, and iterations. #TalkingAboutTesting #TATSchool #PerformanceTestingWithK6 #k6 #PerformanceTesting

👨‍🏫 Remember to tag me in your post. Here is my LinkedIn profile.

Quiz

Question 1 of 2
Score: 0

A k6 script has no `it()` or `test()` blocks, unlike a Cypress or Playwright spec. Why not?

Score 100% on the quiz to continue