Resource library

QA How-To

Gatling scenario design (2026)

Design robust Gatling scenarios: journeys, feeders, pacing, checks, workload mix, and assertions using the JavaScript SDK for realistic load tests.

22 min read | 2,501 words

TL;DR

Gatling scenario design turns a business journey into protocol steps, session state, data feeders, pacing, and a workload mix that mirrors production intent. Keep scenarios focused, validate correctness first, inject open or closed load deliberately, and assert errors plus percentiles before you trust any chart.

Key Takeaways

  • Design scenarios from business journeys and telemetry, not from a raw HAR replay of every browser request.
  • One scenario equals one coherent user goal; compose traffic mix with multiple populations instead of mega-scripts.
  • Prove checks, correlation, and data uniqueness at one user before scaling injection.
  • Choose open vs closed injection from real arrival behavior, and document think time assumptions.
  • Name requests stably so reports remain readable across runs and releases.
  • Separate smoke, baseline, peak, and endurance scenario variants with shared building blocks.
  • Treat scenario code as product: review it, version it, and retire paths the business no longer runs.

Gatling scenario design is the craft of modeling realistic user and system behavior as code so a load test answers a business question. Tools, injection helpers, and pretty reports cannot rescue a scenario that hits the wrong endpoints, reuses one account for every user, or treats a marketing pixel storm as application traffic.

This guide goes beyond first-run syntax. You will design journeys, structure simulations, choose feeders and pacing, mix populations, apply checks and correlation, and keep scenarios maintainable in CI. Examples use the Gatling JavaScript SDK patterns consistent with current Gatling JS projects. For install and first simulation basics, pair this with Gatling basics for testers.

TL;DR

Design decision Prefer Avoid
Scenario scope One business goal per scenario One script that does every feature
Request set Essential owned APIs Full browser HAR including third parties
Data Unique, resettable records Shared admin account for all VUs
Pacing Documented think time Zero pause "max hammer" as default
Injection Open or closed from real model Random large user count
Success criteria Checks + assertions Latency charts without error gates
Naming Stable business labels Dynamic names with user ids

1. Gatling Scenario Design Goals

A good scenario is a hypothesis engine. It should make it possible to accept or reject statements such as:

  • Checkout can sustain N arrivals per second with p95 under the objective and error rate under the limit.
  • Search remains stable when browse traffic is 80% of sessions and checkout is 20%.
  • Token refresh behavior does not collapse during a 60-minute endurance run.

Gatling scenario design therefore starts with stakeholders, not with injectOpen. Interview product, SRE, and analytics partners. Collect peak arrival estimates, concurrency observations, SLA/SLO documents, and critical journey definitions. Write the question on top of the simulation file as a comment so future editors know why the profile exists.

Scenario design also defines what you will not model. Out of scope examples: third-party analytics POSTs you do not own, bot traffic you cannot authorize, or admin bulk exports that skew averages when mixed into shopper load.

2. Map Journeys Before Writing Code

Translate user goals into protocol steps:

  1. List the user intent (browse product, complete checkout, refresh dashboard).
  2. Capture the network evidence (browser DevTools, access logs, API gateway traces).
  3. Keep requests that change state or are required for the intent.
  4. Drop decorative calls unless they are part of the capacity risk you own.
  5. Note auth, idempotency keys, CSRF tokens, and pagination needs.
  6. Identify dynamic values that require correlation.

Example journey sketch for checkout:

Goal: Complete guest checkout for one in-stock SKU
1. GET  /catalog/products/{sku}          -> check 200, save price
2. POST /carts                           -> check 201, save cartId
3. POST /carts/{cartId}/items            -> check 200
4. POST /checkouts                       -> check 201, save checkoutId
5. POST /checkouts/{checkoutId}/pay      -> check 200, assert orderNumber
Pacing: 2s after catalog, 5s before pay (think time)
Data: unique sku list, payment test tokens from secure store

This sketch becomes the acceptance checklist for the scenario code review. If a step is missing from the sketch, it should not appear as a surprise in the simulation.

3. Simulation Structure That Scales With the Suite

Keep a clear layout:

gatling-perf/
├── package.json
├── resources/
│   ├── skus.csv
│   └── users.csv
└── src/
    ├── protocols.js
    ├── chains/
    │   ├── browse.js
    │   └── checkout.js
    └── checkout-mix.gatling.js

Shared protocol module example:

import { http } from "@gatling.io/http";

export function storeProtocol(baseUrl) {
  return http
    .baseUrl(baseUrl)
    .acceptHeader("application/json")
    .contentTypeHeader("application/json")
    .userAgentHeader("gatling-qa-scenario/1.0");
}

Composable chain example:

import { exec, pause } from "@gatling.io/core";
import { http, status, jsonPath } from "@gatling.io/http";

export const openProduct = exec(
  http("GET product")
    .get("/catalog/products/#{sku}")
    .check(status().is(200))
    .check(jsonPath("$.id").saveAs("productId"))
).pause(1, 3);

Composable chains keep Gatling scenario design readable. Prefer small, named fragments over copy-paste blocks. Do not abstract so early that newcomers cannot see the HTTP calls.

4. Scenario Boundaries and Traffic Mix

One scenario should represent one coherent population behavior. Compose mixes at setUp:

import {
  constantUsersPerSec,
  rampUsers,
  scenario,
  simulation,
  global
} from "@gatling.io/core";
import { http, status } from "@gatling.io/http";
import { storeProtocol } from "./protocols.js";

export default simulation((setUp) => {
  const baseUrl = "https://api-ecomm.gatling.io";
  const httpProtocol = storeProtocol(baseUrl);

  const browse = scenario("Browse heavy")
    .exec(
      http("GET session")
        .get("/session")
        .check(status().is(200))
    );

  const lightCheckout = scenario("Checkout light")
    .exec(
      http("GET session checkout")
        .get("/session")
        .check(status().is(200))
    );

  setUp(
    browse.injectOpen(
      rampUsers(20).during(30),
      constantUsersPerSec(5).during(120)
    ),
    lightCheckout.injectOpen(
      rampUsers(5).during(30),
      constantUsersPerSec(1).during(120)
    )
  )
    .protocols(httpProtocol)
    .assertions(
      global().failedRequests().percent().lt(1),
      global().responseTime().percentile(95).lt(800)
    );
});

Why mix matters: a 100% checkout scenario overstates write load and understates cache-friendly read traffic. A 100% browse scenario can hide payment bottlenecks. Use analytics or access logs to pick proportions, then document the percentage assumptions.

5. Session, Correlation, and Checks

Virtual users carry a session map. Correlation means extracting a value from one response and reusing it later. Without correlation, scenarios hard-code IDs and fail in every non-trivial environment.

import { exec, pause } from "@gatling.io/core";
import { http, status, jsonPath } from "@gatling.io/http";

export const createCartAndAddItem = exec(
  http("POST create cart")
    .post("/carts")
    .body(StringBody("{}"))
    .check(status().is(201))
    .check(jsonPath("$.id").saveAs("cartId"))
)
  .pause(1)
  .exec(
    http("POST add item")
      .post("/carts/#{cartId}/items")
      .body(StringBody('{"sku":"#{sku}","qty":1}'))
      .check(status().is(200))
      .check(jsonPath("$.items[0].sku").isEL("#{sku}"))
  );

Check strategy for Gatling scenario design:

  • Always check status on state-changing calls.
  • Check a business field, not only HTTP 200.
  • Fail fast when auth fails so you do not flood the API with unauthorized noise.
  • Keep check severity aligned with the question. A missing optional recommendation block may be a warning in functional testing but irrelevant to capacity if you intentionally skip it.

Import StringBody from the correct Gatling JS module used by your SDK version when you copy fragments into a real project, and align package versions across @gatling.io/core, @gatling.io/http, and @gatling.io/cli.

6. Feeders and Test Data Design

Feeders supply per-user or per-iteration data. Design data with the same care as the HTTP steps.

CSV example resources/skus.csv:

sku,category
SKU-100,shoes
SKU-101,shoes
SKU-200,bags

Usage pattern:

import { csv, scenario } from "@gatling.io/core";

const skus = csv("skus.csv").circular;

const browseSku = scenario("Browse SKU")
  .feed(skus)
  .exec(
    http("GET product by sku")
      .get("/catalog/products/#{sku}")
      .check(status().is(200))
  );

Feeder strategy comparison:

Strategy Behavior Use when
queue (default-style exhaustion) Stops when data ends Exactly-once records required
circular Repeats data Read-mostly catalogs with reusable SKUs
random Picks randomly You need varied cache keys without strict order
shuffle Shuffles then feeds Reduce accidental ordering bias

Data rules:

  • Prefer unique users for write-heavy auth scenarios.
  • Never commit production secrets. Inject secrets from CI or a vault at runtime.
  • Pre-scale data volume to the maximum virtual users and duration you plan.
  • Clean up or isolate write side effects (orders, uploads) with environment strategy.
  • Watch for hidden contention: every user editing the same profile row creates a database fight that may not be realistic.

7. Pacing, Think Time, and Arrival Semantics

Pacing separates "how hard the client hits" from "how users behave."

  • Think time / pause: wait inside a scenario between actions.
  • Arrival rate: how many new users start per second in an open model.
  • Concurrency: how many users are active in a closed model.
import { pause } from "@gatling.io/core";

// Fixed pause
.pause(5)

// Uniform random pause between 2 and 8 seconds
.pause(2, 8)

Design guidance:

  • Human-facing flows almost always need pauses; pure machine integrations may not.
  • If you remove pauses for a stress experiment, label the run as stress, not as "peak hour replica."
  • Do not use pauses to paper over client-side rate limits you should model explicitly.

Open versus closed injection remains a core Gatling scenario design choice:

Model Controls Concurrency when system slows Typical fit
Open (injectOpen) Arrivals over time Tends to rise Shoppers, public APIs
Closed (injectClosed) Active users Stays capped Fixed agent seats, workers

Never mix open and closed injection steps inside the same scenario injection definition. If you need both behaviors, use separate scenarios.

8. Workload Profiles as Scenario Variants

Keep one logical journey with multiple injection wrappers:

Profile Purpose Duration Assertion style
Smoke Prove script correctness seconds-minutes Zero failed requests
Baseline Compare releases tens of minutes p95 + errors
Peak Validate known busy period match business peak window SLO-aligned
Stress Find breaking region ramp beyond peak Diagnostic, may expect errors
Endurance Soak for leaks/degradation hours Trend-aware limits
Spike Sudden arrival change short extreme burst Recovery observations

Implement variants with parameters rather than forked copies of the whole journey. Parameterize base URL, users, duration, and assertion thresholds through environment variables or Gatling parameters so CI can run smoke while night jobs run peak.

9. Realistic Auth and State Modeling

Auth mistakes destroy scenario credibility.

Common patterns:

  1. Login once per virtual user, reuse token for scenario duration.
  2. Login once then refresh when endurance exceeds token TTL.
  3. Pre-mint tokens offline for pure API capacity tests when login is out of scope.
  4. OAuth client credentials for service-to-service scenarios.

Design questions:

  • Does production traffic include login rate as a first-class cost, or are most calls already authenticated?
  • Are sessions sticky to a region or device?
  • Will concurrent logins for one account trigger fraud rules?

If login is expensive and rare in production, a scenario that logs in before every short loop overstates auth load and understates business API load. Split "auth capacity" and "authenticated business capacity" into different designs when needed.

10. Gatling Scenario Design: Assertions, SLOs, and Report Readability

Scenario design includes the verdict. Assertions belong next to the injection profile.

.assertions(
  global().failedRequests().percent().lt(1),
  global().responseTime().percentile(95).lt(900),
  details("POST add item").responseTime().percentile(99).lt(1500),
  details("POST add item").failedRequests().percent().lt(0.5)
)

Naming rules for readable reports:

  • Use business language: POST add item, not req_12.
  • Keep names stable across releases so trends work.
  • Do not put unbounded dynamic values into names.
  • Align names with dashboard labels when SREs correlate tools.

When a run fails, the scenario should make diagnosis easy: small number of request groups, clear checks, known data set, and documented profile. A 40-request mono-scenario with vague names wastes hours.

11. CI, Environments, and Safety Controls

Wire scenario execution the way you wire functional tests: deterministic install, explicit simulation name, artifact upload, secrets from the platform.

Safety checklist for Gatling scenario design in shared environments:

  • Written approval for target and window.
  • Max RPS and max concurrent users enforced in code defaults.
  • Abort rules when error rate explodes (generator-side or pipeline timeout).
  • Separate performance environment when possible.
  • Generator sizing verified so the injector is not the bottleneck.
  • Change management: scenario PRs reviewed by someone who owns the service.

For broader performance strategy context, see the performance testing roadmap and compare tool tradeoffs in JMeter vs Gatling.

12. Refactors and Anti-Patterns Catalog

Refactor when:

  • The same HTTP fragment is pasted three times.
  • A scenario file exceeds a readable size without sectioning.
  • Product removed a feature still present in load paths.
  • Feeders no longer match schema after an API change.

Anti-patterns:

  • HAR replay without editing.
  • One giant scenario for all personas.
  • Silent retries that hide errors.
  • Checking only status while bodies contain "success": false.
  • Using production personal data in CSV feeders.
  • Changing five injection parameters and the journey in one experiment.
  • Calling the test "realistic" without telemetry-backed proportions.

13. Example: From Sketch to Full Scenario Module

The following expanded example shows how a browse-plus-session fragment can be assembled. Adapt endpoints to your approved environment. The structure is the teaching point for Gatling scenario design reviews.

import {
  csv,
  pause,
  scenario,
  simulation,
  global,
  rampUsers,
  constantUsersPerSec
} from "@gatling.io/core";
import { http, status, jsonPath } from "@gatling.io/http";

export default simulation((setUp) => {
  const httpProtocol = http
    .baseUrl("https://api-ecomm.gatling.io")
    .acceptHeader("application/json")
    .contentTypeHeader("application/json");

  const skus = csv("skus.csv").circular;

  const browseAndSession = scenario("Browse and open session")
    .feed(skus)
    .exec(
      http("GET session")
        .get("/session")
        .check(status().is(200))
        .check(jsonPath("$.sessionId").optional().saveAs("sessionId"))
    )
    .pause(1, 2)
    .exec(
      http("GET product")
        .get("/products")
        .queryParam("sku", "#{sku}")
        .check(status().in(200, 404))
    );

  setUp(
    browseAndSession.injectOpen(
      rampUsers(10).during(20),
      constantUsersPerSec(2).during(60)
    )
  )
    .protocols(httpProtocol)
    .assertions(
      global().failedRequests().percent().lt(5),
      global().responseTime().percentile(95).lt(2000)
    );
});

Notice the deliberate choices: feeder before first use of sku, optional capture when a field may be absent, status allow-list when a catalog miss is possible, and assertions that match a smoke-to-baseline profile rather than a production hard SLO. That is Gatling scenario design in miniature: every line encodes an assumption you can defend.

When 404 is an expected catalog miss for some feeder rows, either clean the feeder or model an explicit branch for misses. Clarity beats cleverness. Silent success on business failure is how false confidence enters release meetings.

14. Collaboration Model: Who Reviews Scenario Changes

Performance scenarios rot when only one specialist understands them. Define a lightweight review model:

  1. Author (QA/SDET): implements journey and profile.
  2. Service reviewer (backend owner): confirms endpoints, payloads, and dangerous writes.
  3. SRE/platform reviewer (for large profiles): confirms environment capacity and observability annotations.
  4. Product advisor (as needed): confirms traffic mix and seasonal peaks.

Pull request templates should require: business question, target environment, estimated RPS, data plan, abort rules, and assertion summary. This process prevents drive-by ramps that surprise on-call engineers.

Version scenarios with the same discipline as application code. Tag releases of the performance suite when you compare historical baselines. A chart from six months ago is meaningless if the journey silently changed. Store the commit SHA of both application and scenario suite in every formal report package.

15. Connecting Scenarios to Observability

A scenario that cannot be found in application metrics wastes analysis time. Before a major run:

  • Emit a run id in a custom header such as X-Load-Test-Run if the platform allows it.
  • Coordinate dashboards for API latency, DB pools, and queue depth.
  • Align clock sources so Gatling timestamps and APM timestamps match.
  • Capture generator metrics (CPU, network) beside application metrics.

During analysis, ask whether degradation starts in the scenario step that mutates state, in authentication, or in a dependency called by only one persona. Multi-scenario design makes that isolation possible. Single mega-scenarios blur ownership.

If you already run protocol tests with other tools, keep naming consistent across k6 load testing tutorial scripts and Gatling scenarios so executives see one language for journeys such as checkout and search. Shared vocabulary is part of scenario design maturity, not a soft skill add-on.

Operational tip: schedule a short dry run at 5-10% intensity whenever the scenario changes significantly. Dry runs catch broken correlation and expired test accounts before the expensive window begins. Teams that skip dry runs often burn their only approved peak window on a script bug.

Interview Questions and Answers

Q: How do you approach Gatling scenario design for a new service?

I start with the business question and critical journeys, map protocol steps from telemetry, define data and auth needs, implement a one-user scenario with checks, then add pacing and an evidence-based injection profile. Assertions and report naming are part of the design, not afterthoughts.

Q: Open or closed workload for an ecommerce site?

Usually open, because shoppers arrive independently. I still validate against analytics. I would use closed models for fixed concurrent agent tools or limited seat systems. I document the choice in the simulation.

Q: How do you keep scenarios maintainable?

I use small chains, stable request names, external feeders, parameterized profiles, and code review. I delete obsolete journeys quickly. I avoid premature frameworks that hide the HTTP intent.

Q: What makes a scenario unrealistic?

Missing think time for human flows, wrong traffic mix, shared mutable data collisions, hard-coded tokens, inclusion of unowned third-party calls, and login patterns that do not match production session lifetimes.

Q: How do feeders affect results?

Feeders control cache locality, contention, and authorization scope. Circular reuse of a few SKUs may overstate cache hits. A tiny user pool may serialize on account locks. I size and uniquify data for the workload hypothesis.

Q: Where do checks belong in design?

On every critical step, especially those producing IDs for later calls. Checks protect metric validity. A fast 500 page can otherwise look like great latency.

Q: How do you design multi-scenario mixes?

I assign each persona its own scenario and injection profile, match proportions to analytics, and assert both globally and on critical request details. I keep personas separate so reports explain who suffered.

Common Mistakes

  • Jumping to 1,000 users before a one-user path passes checks.
  • Treating HAR export as a finished scenario.
  • Mixing open and closed injection steps incorrectly.
  • Forgetting pauses and calling the result "peak production."
  • Reusing one credit card token and one user for all write traffic.
  • Dynamic request names that shatter charts into dust.
  • No assertions, only manual screenshot of a graph.
  • Auth login on every loop without product justification.
  • Running destructive write scenarios without cleanup or isolation.
  • Ignoring generator CPU limits while blaming the application.
  • Copying thresholds from a blog instead of service objectives.
  • Letting dead scenarios rot until nobody trusts the suite.

Conclusion

Gatling scenario design is product thinking expressed as protocol code. Focused journeys, honest data, deliberate pacing, correct injection models, and strict checks produce evidence you can defend in a release call. Pretty ramps cannot compensate for the wrong question.

Next step: pick one revenue journey, write a ten-line protocol sketch, implement it as a single-user Gatling JS scenario with checks, then add a small open workload and assertions. Only after that baseline is trusted should you build multi-persona mixes and longer profiles.

Interview Questions and Answers

Walk through your Gatling scenario design process.

I define the business question, map journeys to protocol steps, specify data and auth, implement checks and correlation, prove one user, then add pacing and injection. I finish with assertions, stable naming, and safety limits for the environment.

How do you model multiple user types?

I create one scenario per persona with its own feeder needs and injection profile, then combine them in setUp using proportions from analytics. This keeps reports interpretable and lets me scale personas independently.

Explain correlation in Gatling.

Correlation extracts dynamic values such as cart IDs or tokens using checks, stores them in the virtual user session, and reuses them in later requests. Without it, scenarios break on environments where IDs are not static.

When would you use a closed workload?

When the real system has a fixed active population, such as a limited agent desktop pool. Closed models hold concurrency while throughput reacts to response time. I avoid using closed as a casual substitute for arrival-driven traffic.

How do you validate that a scenario is realistic?

I compare request mix, payload shapes, auth frequency, and think time to production telemetry. I review failures and cache behavior at low load, then confirm the generator can produce the intended arrival rate without saturation.

What request naming standard do you use?

Stable, business-readable names that group identical operations, such as GET product and POST create order. I avoid user-specific suffixes so percentiles remain statistically meaningful.

How do you handle test data for large runs?

I pre-provision enough unique records for users and duration, keep secrets out of CSV, choose feeder strategies deliberately, and define cleanup or isolation for write side effects. Data design is part of scenario design reviews.

Frequently Asked Questions

What is Gatling scenario design?

It is the process of modeling realistic journeys, data, pacing, and workloads as Gatling scenarios so results answer a business performance question. Good design prioritizes correctness and representativeness before high virtual user counts.

How many requests belong in one scenario?

Include the essential protocol steps for one user goal. If steps represent a different persona or goal, move them to another scenario and mix at setUp. Oversized scenarios are hard to tune and hard to diagnose.

Should I replay a full browser HAR in Gatling?

Usually no. HAR files include third-party calls, caching quirks, and asset noise. Extract the owned API sequence that represents business work, then model think time and data explicitly.

What feeder strategy should I use?

Use queue-like exhaustion when records must be unique and finite, circular when reuse is acceptable, and random when you need varied keys without strict order. Match the strategy to cache and contention semantics.

How do I choose think time?

Base pauses on analytics or moderated usability evidence when available. If you lack data, pick a documented conservative range and label it as an assumption. Do not default to zero for human flows.

Can one simulation hold multiple scenarios?

Yes. Multi-scenario setUp is the standard way to express traffic mix. Each scenario can use a different injection profile while sharing protocols and helper chains.

How do assertions relate to scenario design?

Assertions encode the success definition for the designed workload. Without them, scenario design stops at traffic generation. Include error and percentile limits aligned to SLOs.

Related Guides