Skip to content

02 — Core Concepts

Simulation anatomy

A Gatling simulation is a class that extends Simulation. It has three parts:

Simulation
├── Protocol definition   — how to talk to the system (HTTP base URL, headers, …)
├── Scenario(s)           — what virtual users do (steps)
└── setUp block           — wire scenarios + protocols + injection profiles

Java skeleton

public class MySimulation extends Simulation {

    // 1. Protocol
    HttpProtocolBuilder httpProtocol = http.baseUrl("https://api.example.com");

    // 2. Scenario
    ScenarioBuilder scn = scenario("My scenario")
        .exec(http("Home").get("/"));

    // 3. setUp — runs once when the simulation starts
    {
        setUp(
            scn.injectOpen(rampUsers(100).during(Duration.ofSeconds(30)))
        ).protocols(httpProtocol);
    }
}

Kotlin skeleton

class MySimulation : Simulation() {

    val httpProtocol = http.baseUrl("https://api.example.com")

    val scn = scenario("My scenario")
        .exec(http("Home").get("/"))

    init {
        setUp(
            scn.injectOpen(rampUsers(100).during(30))
        ).protocols(httpProtocol)
    }
}

Virtual users

Each virtual user is a lightweight coroutine (not a thread). Gatling can run tens of thousands of concurrent virtual users on a single machine because they share a small thread pool.

Virtual user lifecycle:
  start → execute scenario steps → end
  Each step is async; the engine schedules them without blocking OS threads.

Scenario

A ScenarioBuilder is a blueprint — it describes what each user will do. The actual users are created later by the injection profile.

ScenarioBuilder scn = scenario("Browse shop")
    .exec(http("Home page").get("/"))
    .pause(2)                           // think time
    .exec(http("Products").get("/products"))
    .pause(1, 3)                        // random pause between 1-3 s
    .exec(http("Product detail").get("/products/42"));

Session

Each virtual user has its own Session — a key/value map that persists for the lifetime of that user's scenario run. It is used to:

  • Store extracted response values (e.g. auth tokens)
  • Drive conditional logic
  • Pass data between steps

See 08 — Session for details.


Checks

After every request Gatling can verify the response. A check failure marks the request as KO (failed) in the report but does not stop the simulation by default.

http("Login").post("/login")
    .check(status().is(200))
    .check(jsonPath("$.token").saveAs("authToken"))

See 06 — Checks for details.


Protocol

The protocol builder configures shared behaviour for all requests:

HttpProtocolBuilder httpProtocol = http
    .baseUrl("https://api.example.com")
    .acceptHeader("application/json")
    .contentTypeHeader("application/json")
    .userAgentHeader("Gatling/3.11");

One simulation can use multiple protocols assigned to different scenarios.


setUp block

setUp is where everything is assembled. It returns an SetUp object that accepts global assertions:

{
    setUp(
        scn.injectOpen(rampUsers(200).during(Duration.ofSeconds(60)))
    )
    .protocols(httpProtocol)
    .assertions(
        global().responseTime().percentile(95).lt(500),  // p95 < 500 ms
        global().successfulRequests().percent().gt(99.0) // >99% success rate
    );
}

If any assertion fails, the simulation exits with a non-zero code — handy for CI.


Request naming and grouping

Request names appear in the report. Use descriptive names and group related requests:

scenario("Checkout flow")
    .group("Browse").on(
        exec(http("Home").get("/"))
        .exec(http("Search").get("/search?q=boots"))
    )
    .group("Checkout").on(
        exec(http("Add to cart").post("/cart"))
        .exec(http("Pay").post("/checkout/pay"))
    );

Groups aggregate metrics separately in the report.