DEV Community

Elvin Aghalarov
Elvin Aghalarov

Posted on Originally published at hoofbeat.dev

The same load test in k6, Locust, Artillery, Gatling and JMeter

Comparison posts usually list features. Let's do something more useful: write the same test in five tools and see how each one feels.

The test: 50 new users arrive every second for 5 minutes, each requests /products. That's an arrival rate (open model), which is how real traffic behaves: new visitors keep coming whether or not your server is keeping up. (Why that matters.)

k6 (JavaScript)

import http from 'k6/http';

export const options = {
  scenarios: {
    browse: {
      executor: 'constant-arrival-rate',
      rate: 50,
      timeUnit: '1s',
      duration: '5m',
      preAllocatedVUs: 100,
    },
  },
  thresholds: { http_req_duration: ['p(95)<500'] },
};

export default function () {
  http.get('https://staging.example.com/products');
}
Enter fullscreen mode Exit fullscreen mode
k6 run browse.js
Enter fullscreen mode Exit fullscreen mode

Feels like: writing a small program. Thresholds are built in, so CI pass/fail is one line. Very efficient Go engine. Distributed runs need Kubernetes (k6-operator) or Grafana Cloud.

Locust (Python)

from locust import HttpUser, task, between

class Shopper(HttpUser):
    wait_time = between(2, 5)

    @task
    def browse(self):
        self.client.get("/products")
Enter fullscreen mode Exit fullscreen mode
locust -f locustfile.py --host https://staging.example.com \
  --headless -u 500 -r 50 -t 5m
Enter fullscreen mode Exit fullscreen mode

Feels like: plain Python, which makes complex user behaviour easy. Note the model: Locust spawns a fixed number of users (-u 500) that loop with think time, a closed model, so the request rate drops if the server slows down. A built-in web UI shows live charts.

Artillery (YAML)

config:
  target: https://staging.example.com
  phases:
    - duration: 300
      arrivalRate: 50
scenarios:
  - flow:
      - get:
          url: /products
Enter fullscreen mode Exit fullscreen mode
npx artillery run browse.yml
Enter fullscreen mode Exit fullscreen mode

Feels like: configuration, not code. Arrival rates are the default. Custom logic goes into JavaScript hooks, which gets messy for complicated flows.

Gatling (Java DSL)

import static io.gatling.javaapi.core.CoreDsl.*;
import static io.gatling.javaapi.http.HttpDsl.*;

import io.gatling.javaapi.core.*;
import io.gatling.javaapi.http.*;

public class BrowseSimulation extends Simulation {
  HttpProtocolBuilder httpProtocol = http.baseUrl("https://staging.example.com");

  ScenarioBuilder scn = scenario("Browse")
      .exec(http("products").get("/products"));

  {
    setUp(scn.injectOpen(constantUsersPerSec(50).during(300)))
        .protocols(httpProtocol);
  }
}
Enter fullscreen mode Exit fullscreen mode

Feels like: a typed DSL with great HTML reports and very high throughput per machine. Natural for JVM teams; also available in Scala, Kotlin and JavaScript.

JMeter (GUI / XML)

No snippet that fits here: a JMeter test is a .jmx XML file you usually build in the GUI (Thread Group → HTTP Request → listeners), then run headless:

jmeter -n -t browse.jmx -l results.jtl
Enter fullscreen mode Exit fullscreen mode

Feels like: the veteran. Supports almost any protocol (HTTP, JDBC, JMS, FTP, LDAP) with a huge plugin ecosystem. Thread Groups are closed-model by default; arrival rates need a plugin (Concurrency/Arrivals Thread Group). Large XML test plans are hard to review in pull requests.

Side by side

Tests in Default model Distributed Best for
k6 JavaScript Both k6-operator / cloud Dev teams, CI
Locust Python Closed Built-in workers Python teams, complex behaviour
Artillery YAML + JS Open Cloud runners Quick scenarios, Node teams
Gatling Java/Scala/Kotlin/JS Both Enterprise edition JVM teams, very high load
JMeter GUI / XML Closed Controller + workers Exotic protocols

What none of them do for you

All five generate load well. What they leave to you:

  • Infrastructure: running enough machines, in the right regions, for big tests.
  • Realistic flows: chaining tokens, CSRF, per-user test data.
  • Interpretation: a wall of percentiles doesn't say why checkout got slow.

That gap is why I'm building Hoofbeat: describe the test in plain words (or import a HAR file, OpenAPI spec or Postman collection), and it runs on cloud load generators in the regions you pick, then explains the result per step. But if your team loves tests as code, k6 or Gatling are excellent choices, and many teams use a code tool and a hosted one side by side.

The longer comparison is on the Hoofbeat blog.

Top comments (0)