skip to content

In a Gatling simulation file written in JavaScript or TypeScript, what must the module export, and where does the setUp function it calls come from?

level: middleimportance: must knowfreq 40%

answer

  1. One default export per file
  2. A callback, not a subclass
  3. simulation() is imported from the core package
  4. setUp arrives as the callback's argument

basics

~20 s

The module's default export must be the value returned by simulation(...), imported from @gatling.io/core. That call takes one callback, and Gatling hands setUp into it as an argument — there is no class to extend and nothing to construct.

solid answer

~50 s

A JavaScript or TypeScript simulation file is a module whose default export is a `simulation(...)` call: `export default simulation((setUp) => { ... })`. Inside that callback you call `setUp(...)` once with the injected populations, chaining `.protocols(...)` onto it; the protocol and the scenarios are ordinary values you may build inside the callback or at module top level and merely reference. `setUp` is not imported — it is the parameter Gatling passes into your callback, which is why it is the one part that cannot move out. That is the whole difference in shape from the JVM SDKs, where you declare a class that extends `Simulation` and call the inherited `setUp` from its constructor. Because a module has no class name, the tooling identifies your simulation by **file**, which is why exporting it as the module default is what makes it discoverable at all.

code

typescript · 9 lines
typescript
import { atOnceUsers, scenario, simulation } from "@gatling.io/core";
import { http } from "@gatling.io/http";

export default simulation((setUp) => {
  const httpProtocol = http.baseUrl("https://api-ecomm.gatling.io");
  const scn = scenario("checkout").exec(http("cart").get("/cart"));

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

go deeper

for a junior

Be ready to write the file shape from memory: import simulation from the core package, export default a simulation call, and call setUp inside the callback — the protocol and the scenario may sit there too, or above it.

for a middle

Explain that setUp is the callback's parameter rather than an import or an inherited method, and that the default export is what makes the file discoverable.

for a senior

Show how the module shape changes project conventions — one simulation per file, shared protocol and helper code in ordinary modules, and no package structure to mirror.

for a principal

Own the house style for a JavaScript suite: what belongs in shared modules versus inside each callback, and how you stop the callback growing into an unreviewable block.

On the JVM, a Gatling simulation is a **class**: you extend `Simulation`, and the framework instantiates it to run it. In JavaScript and TypeScript there is no class in the picture. A simulation is a **module** whose default export is the value a `simulation(...)` call returns. ```typescript import { constantUsersPerSec, scenario, simulation } from "@gatling.io/core"; import { http } from "@gatling.io/http"; export default simulation((setUp) => { const httpProtocol = http.baseUrl("https://api-ecomm.gatling.io").acceptHeader("application/json"); const scn = scenario("Scenario").exec(http("Session").get("/session")); setUp(scn.injectOpen(constantUsersPerSec(2).during(60))).protocols(httpProtocol); }); ``` ## The three parts of the contract 1. **`simulation` is imported from `@gatling.io/core`**, alongside the rest of the DSL. It is an ordinary named export, not a global. 2. **It takes exactly one callback**, and Gatling invokes that callback with `setUp`. The registration call has to happen inside it, because `setUp` exists nowhere else; the protocol, the headers, the feeders and the scenarios are ordinary values that may be declared inside the callback or at module top level and merely referenced. Gatling's own recorder generates exactly that top-level shape — protocol, headers and scenario above the export, and only the `setUp(...)` call inside it. 3. **The module default-exports the result.** Gatling's own guidance is explicit about why: export the simulation as the module default *so the CLI can pick it up automatically*. ## Where `setUp` comes from `setUp` is the **parameter of your callback**. You do not import it and you do not inherit it; Gatling passes it in when it evaluates the module. That is the single most common point of confusion for someone arriving from Java or Scala, where `setUp` is a method inherited from the `Simulation` base class and is called from the constructor. The consequence is that `setUp` **only exists inside the callback**. Code at module top level cannot reach it, so the registration call has to live there. Everything else is free: protocol objects, feeder definitions and helper chains can be built inside the callback, or built outside and merely referenced inside. Most documentation samples keep them inside as a matter of style; the recorder puts them outside. | | JVM SDKs (Java, Kotlin, Scala) | JavaScript / TypeScript SDK | |---|---|---| | unit of a simulation | a class extending `Simulation` | a module with a default export | | how you get `setUp` | inherited method, called in the constructor | argument passed into your callback | | what identifies it to the tooling | the class name | the file name | | entry point of the DSL | static imports of the `CoreDsl` / `Predef` surfaces | named imports from `@gatling.io/core` and `@gatling.io/http` | ## Why the shape follows from the tooling A module has no fully qualified name to select on, so the CLI cannot ask you for one. It resolves a simulation by **file**, matching a name pattern in a source folder, and reads the module's default export to get the simulation itself. The export therefore is not a stylistic choice: a file that exports its simulation under a *name* rather than as the default gives the CLI nothing to load, and a file that defines a simulation but exports nothing is invisible even when it sits in exactly the right folder. The same reasoning explains the flat structure. The JVM formats put simulations in packages; the JavaScript and TypeScript formats have no package concept at all, which is visible even in Gatling's recorder — it flags the JVM output formats as having packages and the JavaScript and TypeScript formats as not. ## What still behaves exactly as it does on the JVM The DSL is the same under different syntax, wherever you build it. Scenario and protocol builders are still **immutable**, so chaining returns a new builder and you must keep the value. `setUp` is still called **once**, with all the populations you want to run, and `.protocols(...)` still attaches either globally, chained onto the result of that call, or to a single injected population. The step names in an injection profile are the same names the JVM SDKs use. In TypeScript the same file gets its types from the same packages you already installed — a session type, for example, is imported from `@gatling.io/core` beside the functions — so the module shape is identical in both languages and only the annotations differ. ## The failure modes to recognise - **`export const mySimulation = simulation(...)`** — valid module code, invisible to the tooling. Make it the default export. - **Calling `setUp(...)` at module top level** — there is no `setUp` in scope there; it exists only as the callback's parameter. - **Reaching for `extends Simulation`** — nothing to extend. The class-based shape belongs to the JVM SDKs. - **Two simulations in one file** — the tooling names a simulation by its file and loads the module default, so put the second one in its own file.

  • Can one file hold two Gatling simulations?
    Treat it as one per file. The tooling identifies a simulation by its file and loads that module's default export, and a module has only one default — so a second `simulation(...)` value in the same file has no way to be selected. Give it its own file instead.
  • Why does a JavaScript simulation not extend a Simulation class the way a Java one does?
    There is no class in the JavaScript/TypeScript shape at all. The SDK gives you a `simulation` function that takes your callback and passes `setUp` into it, so the framework never has to construct anything of yours. That also removes the constructor-timing rule the JVM SDKs have.

saying these in an interview costs you the question

  • Writing a class that extends Simulation in a TypeScript file
  • Exporting the simulation under a name instead of as the default
  • Calling setUp at module top level, outside the callback
  • Exporting the scenario builder rather than the simulation