Back to Performance Testing
Q3Question 3 of 3

Tools used for performance testing

Key Concepts
  • JMeter is the mature open-source GUI-based tool with broad protocol support and a huge plugin ecosystem
  • Gatling is code-based (Scala/Java/Kotlin DSL) with an efficient async engine and detailed HTML reports
  • k6 is JavaScript-based, developer-friendly, and designed for CI/CD with built-in threshold assertions
  • Locust is Python-based with simple distributed mode, ideal for Python teams
  • Choose by team skills, scripting vs GUI preference, reporting needs, and CI/CD integration requirements
  • The tool only generates load: interpreting results correctly with percentiles and server metrics is the real skill

Definition

JMeter is the mature GUI-based open-source standard, Gatling is code-based in Scala with great reports, k6 is JavaScript-based and CI/CD-friendly, and Locust is Python-based, and you choose by team skills, scripting versus GUI, reporting, and pipeline fit.
  • JMeter – Mature open-source GUI-based tool with broad protocol support and a huge plugin ecosystem. Run in non-GUI mode for scale.
  • Gatling – Code-based scenarios in Scala/Java/Kotlin DSL with an efficient async engine and detailed HTML reports.
  • k6 – JavaScript-based, developer-friendly, with CI/CD-first design and built-in threshold assertions that fail the pipeline.
  • Locust – Python-based, defining user behavior as Python classes, with a simple distributed mode and live web UI.
  • Choice criteria – Team skills, scripting vs GUI, reporting, and CI/CD fit determine the right tool.

To stress-test the National Bank of India systems you need a tool that can pretend to be thousands of customers at once and time how the bank responds. JMeter is the classic point-and-click tool. Gatling is for teams who prefer writing load scenarios as code and want beautiful reports. k6 is loved by developers because you write in JavaScript and it slots into the build pipeline. Locust is the choice for Python teams.

Understanding the Concepts

What do all performance tools share?

All tools share a core model: define a scenario (a sequence of requests), a workload (virtual users, ramp, duration), generate concurrent load, and collect metrics.

DIAGRAM
  Scenario + Workload  ──>  Tool  ──>  Metrics
  (what to request)         │          (response time,
  (how many users)          │           throughput,
  (ramp pattern)            │           error rate)
                            ▼
                       Report / Dashboard

What is JMeter?

JMeter is the long-established Apache project with a GUI for building test plans visually.

BASH
# Run JMeter in non-GUI mode for real load
jmeter -n -t transfer-test-plan.jmx \
  -l results.jtl \
  -e -o report/

# Generate HTML report from results
jmeter -g results.jtl -o report/
XML
<!-- JMeter test plan structure (simplified) --syn-kw">>
syn-kw"><TestPlansyn-kw">>
  syn-kw"><ThreadGroupsyn-kw">>
    syn-kw"><numThreadssyn-kw">>500syn-kw"></numThreadssyn-kw">>
    syn-kw"><rampTimesyn-kw">>120syn-kw"></rampTimesyn-kw">>
    syn-kw"><HTTPSamplersyn-kw">>
      syn-kw"><pathsyn-kw">>/api/transfersyn-kw"></pathsyn-kw">>
      syn-kw"><methodsyn-kw">>POSTsyn-kw"></methodsyn-kw">>
    syn-kw"></HTTPSamplersyn-kw">>
    syn-kw"><ResponseAssertionsyn-kw">>
      syn-kw"><testStringsyn-kw">>200syn-kw"></testStringsyn-kw">>
    syn-kw"></ResponseAssertionsyn-kw">>
  syn-kw"></ThreadGroupsyn-kw">>
syn-kw"></TestPlansyn-kw">>

What is Gatling?

Gatling writes scenarios as code with an efficient async engine.

SCALA
// Gatling scenario in Scala
import io.gatling.core.Predef._
import io.gatling.http.Predef._
import scala.concurrent.duration._

class TransferSimulation extends Simulation {
  val httpProtocol = http
    .baseUrl("https://staging.nationalbank.in")
    .header("Content-Type", "application/json")

  val scn = scenario("Transfer Journey")
    .exec(
      http("Login")
        .post("/api/auth/login")
        .body(StringBody("""{"user":"test","pass":"test"}"""))
        .check(jsonPath("$.token").saveAs("token"))
    )
    .pause(1)
    .exec(
      http("Transfer")
        .post("/api/transfer")
        .header("Authorization", "Bearer ${token}")
        .body(StringBody("""{"from":"ACC-001","to":"ACC-002","amount":1000}"""))
        .check(status.is(200))
    )

  setUp(
    scn.inject(
      rampUsers(500).during(2.minutes),
      constantUsersPerSec(50).during(10.minutes)
    )
  ).protocols(httpProtocol)
   .assertions(
     global.responseTime.percentile3.lt(200), // p95 < 200ms
     global.failedRequests.percent.lt(1.0)     // error < 1%
   )
}

What is k6?

k6 is JavaScript-based and built for CI/CD.

JAVASCRIPT
// k6 scenario in JavaScript
import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
  stages: [
    { duration: '2m', target: 500 },
    { duration: '10m', target: 500 },
    { duration: '2m', target: 0 },
  ],
  thresholds: {
    http_req_duration: ['p95<200'],
    http_req_failed: ['rate<0.01'],
  },
};

export default function () {
  const login = http.post(`${__ENV.BASE_URL}/api/auth/login`,
    JSON.stringify({ user: `user-${__VU}`, pass: 'test' }),
    { headers: { 'Content-Type': 'application/json' } }
  );
  const token = login.json('token');
  sleep(1);

  const transfer = http.post(`${__ENV.BASE_URL}/api/transfer`,
    JSON.stringify({ from: 'ACC-001', to: 'ACC-002', amount: 1000 }),
    { headers: { Authorization: `Bearer ${token}` } }
  );
  check(transfer, { 'transfer 200': (r) => r.status === 200 });
  sleep(1);
}
BASH
# Run k6 with CI thresholds
k6 run --env BASE_URL=https://staging.nationalbank.in perf/transfer.js
# Exits non-zero if thresholds fail -> pipeline fails

What is Locust?

Locust defines user behavior as Python classes.

PYTHON
# locustfile.py
from locust import HttpUser, task, between

class BankUser(HttpUser):
    wait_time = between(1, 3)

    def on_start(self):
        res = self.client.post("/api/auth/login",
            json={"user": "test", "pass": "test"})
        self.token = res.json()["token"]

    @task(3)
    def view_accounts(self):
        self.client.get("/api/accounts",
            headers={"Authorization": f"Bearer {self.token}"})

    @task(1)
    def transfer(self):
        self.client.post("/api/transfer",
            json={"from": "ACC-001", "to": "ACC-002", "amount": 1000},
            headers={"Authorization": f"Bearer {self.token}"})
BASH
# Run Locust
locust -f locustfile.py --host=https://staging.nationalbank.in
# Opens web UI at localhost:8089 to control the test

How do they compare?

| Feature | JMeter | Gatling | k6 | Locust |
|---|---|---|---|---|
| Language | GUI/XML | Scala/Java/Kotlin | JavaScript | Python |
| Authoring | Visual + plugins | Code | Code | Code |
| Engine | Thread-per-user | Async/non-blocking | Go engine | gevent |
| CI/CD fit | Good (CLI mode) | Good | Excellent | Good |
| Reports | Plugins, JTL | Rich HTML built-in | CLI + Grafana | Web UI + CSV |
| Learning | Easy (GUI) | Moderate (Scala) | Easy (JS) | Easy (Python) |
| Best for | Enterprise suites | Code-first, reports | CI pipelines | Python teams |

When do you choose each tool?

DIAGRAM
  Team knows GUI + enterprise protocols ──> JMeter
  Team wants code + great reports        ──> Gatling
  Team wants JS + CI/CD thresholds       ──> k6
  Team is Python-centric                 ──> Locust

What are common interview follow-up questions?

  • "Can you use multiple tools?" Yes. JMeter for the established enterprise suite and k6 for modern services in CI is common.
  • "Which is fastest at generating load?" Gatling and k6, because of their async/Go engines. JMeter's thread model is heavier per user.
  • "What matters more than the tool?" Realistic scenarios, production-like data, percentile reporting, and server-side metric correlation.

Visual Overview

DIAGRAM
  Performance Testing Tool Comparison
  ───────────────────────────────────

  JMeter                    Gatling
  ┌──────────────────┐     ┌──────────────────┐
  │ GUI-based         │     │ Code-based        │
  │ Apache project    │     │ Scala/Java DSL    │
  │ Thread-per-user   │     │ Async engine      │
  │ Huge plugin eco   │     │ Rich HTML reports │
  │ XML test plans    │     │ Version-friendly  │
  └──────────────────┘     └──────────────────┘

  k6                        Locust
  ┌──────────────────┐     ┌──────────────────┐
  │ JavaScript        │     │ Python            │
  │ Go engine         │     │ gevent async      │
  │ CI/CD-first       │     │ Web UI for live   │
  │ Threshold gates   │     │ Simple distributed│
  │ Developer-loved   │     │ Python teams      │
  └──────────────────┘     └──────────────────┘

Key Takeaway

All performance tools share the same core: define a scenario and workload, generate concurrent load, and collect metrics. They differ in authoring style, engine efficiency, reporting, and CI/CD fit. JMeter is the mature GUI standard with broad protocols. Gatling is code-first with great reports. k6 is JavaScript-based and CI/CD-native with threshold gates. Locust is Python-based with a live web UI.

I choose by context: JMeter when the team wants GUI and protocol breadth, Gatling for code-first with strong reports, k6 for JavaScript and pipeline integration, Locust for Python teams. The tool only generates load; the real skill is interpreting results with percentiles and server metrics.

At the National Bank of India we used JMeter for the established enterprise suite and k6 for modern services in CI with p95 thresholds that failed the build on regression.

Remember one sentence: choose your performance tool by team skills and CI/CD fit, but remember the tool only generates load, interpreting results correctly is the real skill.

---

Share
Use ← → arrow keys to navigate