skip to content

Profile Assembly

Turning individual steps into a run: the helpers that build a staircase of levels, the one call that registers a population, and the throughput ceiling laid over the whole simulation.

on this pageshow

explore

questions

16

In a Gatling simulation, what does the setUp call do and where must it appear?

level: juniorimportance: must knowfreq 82%

answer

  1. one mandatory call per simulation
  2. runs in the constructor, not a method
  3. binds a scenario to an injection profile
  4. returns a handle for protocols and maxDuration
  5. second call throws Can only call setUp once

basics

~20 s

setUp registers this simulation's populations, each one a scenario welded to an injection profile, and returns a handle for run-wide settings such as protocols and maxDuration. It must be called exactly once, from the Simulation's constructor.

solid answer

~40 s

`setUp` is the one mandatory call in a Gatling simulation. It takes one or more *populations* — a scenario that has been given an injection profile — and registers them as the load this run will produce. It returns a `SetUp` handle whose chained methods configure the whole run: `protocols`, `assertions`, `maxDuration`, `throttle` and the global pause policy. It has to run in the constructor, because Gatling instantiates the simulation class through its no-argument constructor and reads the registration straight afterwards. That is why Scala puts the call bare in the class body, Kotlin inside an `init` block, Java inside an instance initializer, and JavaScript inside the callback of `simulation((setUp) => ...)`, where `setUp` arrives as an argument rather than an inherited method.

code

java · 16 lines
java
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 BasicSimulation extends Simulation {

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

  ScenarioBuilder scn = scenario("basic").exec(http("home").get("/"));

  {
    setUp(scn.injectOpen(atOnceUsers(1))).protocols(httpProtocol);
  }
}

go deeper

for a junior

Be able to point at the setUp line in a simulation and say what each argument is: a scenario that has been given an injection profile.

for a middle

Be ready to explain why the call has to be in the constructor, and to name where it goes in Java, Kotlin, Scala and JavaScript without looking it up.

for a senior

Be ready to debug the two failure modes in someone else's project: a second setUp call, and a setUp that never runs because it sits in an uninvoked helper.

for a principal

Own the decision about how much of a simulation is extracted into shared libraries, given that setUp is the one piece that cannot leave the simulation class.

`setUp` is the single call that turns a pile of Gatling builders into a runnable test. Everything else in a simulation — scenarios, protocol configuration, injection profiles, feeders — can be extracted into helper classes and shared; `setUp` is the one piece that must be present, in this class, exactly once. ## The unit it registers: a population Gatling keeps two ideas deliberately separate. A **scenario** (`scenario("checkout").exec(...)`) describes what one virtual user *does*. An **injection profile** describes *when* virtual users of that scenario start. Neither is load on its own. A **population** is the two welded together, and you weld them by calling an injection entry point on the scenario: - `scn.injectOpen(...)` or `scn.injectClosed(...)` in Java, Kotlin, JavaScript and TypeScript - `scn.inject(...)` in Scala Either way you get back a `PopulationBuilder`, and `PopulationBuilder` is the only type `setUp` accepts. Both a varargs form and a `List` form exist in every SDK, so a simulation that builds its populations programmatically can assemble the list first and still make a single call. ## Where the call must live, and why Gatling never invokes a method you name. It scans for a concrete class assignable to its `Simulation` type, calls that class's **no-argument constructor** by reflection, and then immediately reads the parameters the constructor left behind. Anything not registered by the time the constructor returns does not exist as far as the run is concerned. That one mechanism explains every language's placement: | language | where `setUp` goes | |---|---| | Scala | bare in the class body, which *is* the constructor | | Kotlin | inside an `init { }` block | | Java | inside an instance initializer block `{ ... }`, or the constructor | | JavaScript / TypeScript | inside the callback of `export default simulation((setUp) => { ... })` | The JavaScript and TypeScript case is the one that catches people out. There is no class to extend and no inherited method: `setUp` is **handed to your callback as an argument**. The generated skeletons Gatling's own recorder emits show all four shapes, and each of them ends the same way — `setUp(scn.injectOpen(atOnceUsers(1))).protocols(httpProtocol)`, with the protocol chained onto what `setUp` returned. ## The handle you get back `setUp` returns a `SetUp` object, and unusually for this DSL it is **mutable** — every scenario, chain and population builder around it returns a fresh copy when you chain on it, but `SetUp` mutates the simulation and returns itself. Chaining on it configures the run as a whole: - `protocols(...)` — protocol configuration applied to every registered population - `assertions(...)` — the conditions the finished run is judged against - `maxDuration(...)` — a wall-clock ceiling on the whole run - `throttle(...)` — a throughput ceiling over the whole run - `disablePauses()`, `constantPauses()`, `uniformPauses(...)` and the other pause-policy methods Because each of these returns the same handle, they chain in any order after the closing parenthesis of `setUp(...)`. ## Exactly once — and what happens when it is not The rule is enforced, not merely documented: 1. **Two calls fail on the spot.** The Java API throws `UnsupportedOperationException` with the message *Can only call setUp once*; Scala's version is a `require`, so it throws `IllegalArgumentException` with *requirement failed: setUp can only be called once*. Different exception types, same rule, and it is why you register several populations as several arguments to one call rather than as several calls. 2. **Zero calls fail at startup, not at compile time.** The class compiles perfectly; the run dies while reading the simulation with *No scenario configured, make sure to call setUp.* A `setUp` hidden inside a private helper the constructor never invokes produces exactly this. 3. **Every other registration rule is checked in that same startup read, not inside `setUp`.** The `setUp` call itself enforces only the once-only rule of point 1; the remaining checks run when Gatling reads the simulation after the constructor has returned — the very moment that catches the missing `setUp` of point 2. Populations may not be `null` (the message even suggests a forward-reference problem), no registered scenario may be empty, no scenario name may be empty, and scenario names must be **unique across every registered population**, sequential children included. ## Registration is not execution Nothing is injected when the constructor returns. Gatling takes the registered populations, builds the flow graph, starts the statistics engine and only then begins injecting users. `setUp` does not block, does not return results, and cannot be used to inspect a run — it is a declaration, read once, before anything starts.

  • What happens when a Gatling simulation class never calls setUp at all?
    The class compiles fine and the run dies while reading it, with `requirement failed: No scenario configured, make sure to call setUp.` It is a startup failure rather than a compile error, so a `setUp` buried in a helper method the constructor never invokes is only ever caught at run time.
  • Can a Gatling simulation build its populations dynamically and still obey the one-call rule?
    Yes. Both the Scala and the Java API expose a `List` overload alongside the varargs one, so you can map over configuration, collect the resulting `PopulationBuilder` objects and pass the whole list to a single `setUp` call. Build the list first; never call `setUp` once per element.
  • Why does Gatling require the simulation class to have a public no-argument constructor?
    Because it loads the class reflectively and calls `getConstructor().newInstance()` before reading the registration. A class with only a parameterised constructor cannot be instantiated that way, and abstract classes and interfaces are skipped outright when Gatling scans for simulations.

Think of it as filing a flight plan rather than flying: the constructor hands Gatling the whole plan once, and the tower reads it the moment you stop writing.

saying these in an interview costs you the question

  • Thinking setUp is called once per scenario rather than once per simulation
  • Putting setUp in a helper method the constructor never calls
  • Believing setUp starts the run instead of only declaring it
  • Expecting a missing setUp to be caught by the compiler
open as a page

In Gatling, which builders declare a whole stepped load profile in one expression, and what does each call in that chain set?

level: juniorimportance: must knowfreq 48%

basics

~10 s

Gatling has one staircase builder per workload model: incrementUsersPerSec(rateIncrement) and incrementConcurrentUsers(usersIncrement). Both chain .times(levels).eachLevelLasting(duration), plus optional .separatedByRampsLasting(duration) and .startingFrom(x).

open as a page

In a Gatling simulation, why can a throttle set to 200 requests per second finish the run below 200, and what does enabling that throttle do to the scenario's pauses?

level: middleimportance: must knowfreq 50%

basics

~20 s

A throttle only caps, never raises. Gatling cannot exceed what the injection profile and scenario already offer once pauses are off, so an under-supplied run stays below the ceiling. Enabling one also forces the covered populations' pause policy off.

open as a page

In Gatling, why do Java, Kotlin and JavaScript scenarios call injectOpen or injectClosed while Scala scenarios call a single inject?

level: middleimportance: must knowfreq 52%

basics

~20 s

The entry points sit in different binding layers. Scala's inject infers the model from an implicit InjectionProfileFactory chosen by the step type, while the Java API has no implicits and names the choice instead, as injectOpen and injectClosed.

open as a page

In Gatling, how do you make one setUp run two scenarios one after the other rather than at the same time?

level: middleimportance: must knowfreq 56%

basics

~20 s

Chain the second population onto the first with andThen instead of passing both as separate arguments to setUp. Populations listed side by side start together; an andThen child starts only once every user of its parent has terminated.

open as a page

In Gatling, what load does incrementUsersPerSec(20).times(8).eachLevelLasting(Duration.ofMinutes(2)) actually apply when startingFrom is left off?

level: middleimportance: must knowfreq 38%

basics

~20 s

Levels of 0, 20, 40, 60, 80, 100, 120 and 140 arrivals per second, two minutes each. Omitting startingFrom spends the first two minutes injecting nobody and tops out one increment short, at 140 rather than 160.

open as a page

In a Gatling simulation, which building blocks make up a throttle profile, and what does each one do?

level: juniorimportance: should knowfreq 42%

basics

~10 s

Three chain inside throttle(...): reachRps(target).in(duration) ramps the requests-per-second ceiling to a target, jumpToRps(target) steps to it instantly, and holdFor(duration) holds the current value. Declare the chain on setUp or on one injected population.

open as a page

In a Gatling injection profile, when does the next chained injection step begin?

level: middleimportance: should knowfreq 42%

basics

~20 s

The next step begins as soon as every virtual user the current step defines has started, not when those users have finished. A Gatling injection profile is a schedule for launching users, so consecutive steps deliberately overlap.

open as a page

Why does a Gatling staircase written as incrementUsersPerSec(20).times(8).during(Duration.ofMinutes(2)) fail to compile, and how is the duration spelled instead?

level: middleimportance: should knowfreq 30%

basics

~10 s

The stepped-level builders declare no during method. A staircase spells its durations with eachLevelLasting(d) for each level, and optionally separatedByRampsLasting(d) for the ramps between levels.

open as a page

In a Gatling simulation running two scenarios at once, what does a single throttle declared on setUp cap, and how does that differ from calling throttle on each injected population?

level: seniorimportance: should knowfreq 33%

basics

~20 s

A throttle on setUp is one shared ceiling for the whole run: both scenarios draw on the same per-second budget, first come first served, with no guaranteed split. A throttle per injected population gives each scenario its own ceiling.

open as a page

In a Gatling simulation, which run settings attach to setUp itself rather than to one population, and what wins when both are set?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Assertions and maxDuration exist only on the setUp handle; protocols, the pause policy and throttling exist on both setUp and each population; andThen exists only on a population. Where a protocol is set in both places, the population's setting wins.

open as a page

In a Gatling stepped injection profile, what does separatedByRampsLasting add, and why does the presence of startingFrom change the resulting shape?

level: seniorimportance: should knowfreq 26%

basics

~20 s

It inserts a linear ramp between levels instead of a hard jump. With a non-zero startingFrom the shape is level-then-ramp, ending on a level with no trailing ramp; with it omitted or set to zero the shape is ramp-then-level, opening with a ramp out of zero.

open as a page

Your Gatling run must sit at 200 requests per second across two scenarios, so how would you decide between declaring that rate with throttle and declaring it in the injection profile?

level: principalimportance: should knowfreq 30%

basics

~20 s

Decide by what the number is. A rate the test exists to prove belongs in the injection profile, which keeps think time and the request mix intact. A rate that is only a safety limit belongs in throttle.

open as a page

When would you stop expressing a Gatling capacity profile with the incrementUsersPerSec staircase and hand-chain the injection steps instead?

level: principalimportance: should knowfreq 24%

basics

~20 s

When the profile stops being uniform. The staircase gives one increment, one level duration and one ramp duration for the whole run, so unequal levels, an uneven progression or a longer dwell at the top all force explicit alternating steps.

open as a page

In a Gatling run, what happens to requests the throttle ceiling will not admit yet, and what ends a run whose throttle profile is shorter than its injection profile?

level: middleimportance: nice to knowfreq 26%

basics

~20 s

Nothing is dropped. Excess requests are buffered in an unbounded in-memory queue inside the load generator and replayed on the next one-second tick, risking an OutOfMemoryError. The throttle profile's total duration also bounds the run, stopping it like maxDuration.

open as a page

In Gatling, why does a Kotlin simulation have to write incrementUsersPerSec(20.0) while incrementConcurrentUsers(20) is fine as written?

level: middleimportance: nice to knowfreq 20%

basics

~20 s

The open staircase builder takes a Double and the closed one takes an Int, and startingFrom follows the same split. Kotlin performs no implicit widening from Int to Double, so the open builder needs a decimal literal.

open as a page