Resource library

QA How-To

Gatling basics for testers: Step by Step (2026)

Learn Gatling basics for testers with JavaScript setup, runnable simulations, workload models, checks, assertions, reporting, CI, and interview answers.

23 min read | 3,514 words

TL;DR

Gatling models protocol traffic as code. Define a valid scenario, add checks and data, apply an evidence-based open or closed workload, and enforce error and response-time assertions. Start with one user and scale only after the traffic model is proven.

Key Takeaways

  • Use Gatling for protocol-level load behavior, not browser rendering or visual validation.
  • Prove one virtual user's scenario and checks before increasing the workload.
  • Choose an open arrival model or closed concurrency model from real system behavior.
  • Validate business success so fast error responses do not pollute performance results.
  • Set error and percentile assertions before the run to create an objective verdict.
  • Correlate Gatling observations with application, database, dependency, and generator telemetry.
  • Keep large tests in approved, controlled environments with explicit abort rules.

Gatling basics for testers starts with one practical idea: model realistic traffic at the protocol level, then decide whether the system meets explicit performance goals. Gatling sends HTTP requests efficiently, records response behavior, and produces reports without launching a browser for every virtual user.

This guide uses the current Gatling JavaScript SDK so testers who know TypeScript or JavaScript can build a first simulation quickly. You will set up a project, write a runnable test, add checks and data, choose a workload model, enforce assertions, and interpret results without turning a load generator into a random traffic cannon.

TL;DR

Task Gatling building block Tester decision
Describe a user journey scenario and exec Which requests represent real business work?
Configure HTTP behavior http.baseUrl() and headers Which host, headers, and protocol settings are valid?
Validate responses check What proves a request completed correctly?
Generate arrivals injectOpen What arrival rate matches the production model?
Hold concurrency injectClosed Is the population fixed by design?
Set pass criteria assertions Which error and response-time limits matter?
Investigate a run Gatling logs and HTML report Is the bottleneck in the app, test, data, or infrastructure?

Start with one low-load simulation against an approved environment. Confirm request correctness before increasing traffic. A high request count does not make a useful performance test if the requests are failing, cached unexpectedly, or exercising an unrealistic path.

1. Gatling Basics for Testers: What the Tool Actually Does

Gatling is a load-testing tool that drives virtual users through protocol-level scenarios. For HTTP testing, it builds and sends requests directly. It does not render pages, execute application JavaScript, calculate CSS, or behave like a full browser. That distinction is central to Gatling basics for testers because it determines what the results mean.

A Playwright or Selenium test proves browser behavior. A Gatling simulation measures how services behave when many modeled users or requests arrive. Both can cover a login flow, but they observe different layers. Use a small browser suite for user-facing correctness and a protocol-level load test for throughput, latency, saturation, and error behavior. The Playwright test architecture guide is a useful companion when deciding which checks belong in each layer.

Gatling scenarios are code. A simulation contains protocol configuration, one or more scenarios, workload injection, and assertions. Each virtual user owns session data while executing the scenario. Gatling records request timing and status, then creates a report for local runs.

Do not begin with a target such as "run 1,000 users." Begin with a business question. Examples include whether checkout sustains an agreed arrival rate, whether the search API remains within its latency objective during a ramp, and where errors appear as capacity is approached. The workload, data, duration, and assertions should all support that question.

2. Install the JavaScript SDK and Create the Project

The current Gatling JavaScript toolchain requires a supported LTS Node.js release and npm. Use the versions required by the official SDK documentation for the release you install, commit the lockfile, and keep the three Gatling packages aligned. A minimal setup uses the CLI as a development dependency and the core and HTTP modules as application dependencies.

mkdir gatling-qa-demo
cd gatling-qa-demo
npm init -y
npm install --save-dev @gatling.io/cli
npm install --save @gatling.io/core @gatling.io/http
mkdir src resources

Add scripts to package.json so local and CI execution use the same command:

{
  "scripts": {
    "load:test": "gatling run --simulation basic-api",
    "load:help": "gatling run --help"
  }
}

Gatling discovers simulation files at the root of src when their names end in .gatling.js or .gatling.ts. Feeder files normally live in resources, and local results are written below target/gatling. A compact project looks like this:

gatling-qa-demo/
├── package.json
├── package-lock.json
├── resources/
│   └── users.csv
└── src/
    └── basic-api.gatling.js

Run npx gatling --help after installation. This validates that npm can resolve the local CLI. The first execution may need network access to obtain the matching Gatling runtime. In a controlled corporate network, configure the supported proxy or approved runtime installation process instead of copying an unknown binary into CI.

Keep the load project separate from application runtime dependencies when ownership or release cadence differs. Colocation can still work if it improves review and version alignment. The important point is reproducibility: a clean checkout plus npm ci must produce the same simulation inputs.

3. Understand Simulation Anatomy Before Adding Load

A Gatling JavaScript simulation exports the result of simulation. Inside its callback, you define a protocol, a scenario, and a setup. The protocol centralizes HTTP behavior. The scenario describes ordered user actions. The injection profile says when virtual users start. Assertions turn observed measurements into a pass or fail decision.

Element Purpose Frequent beginner error
Protocol Base URL, common headers, connection behavior Repeating environment details in every request
Scenario Named business flow Mixing unrelated journeys into one long script
Request One protocol operation with a stable label Naming every request "request 1"
Check Validate and optionally capture response data Measuring a fast error page as success
Session Per-user state passed through the scenario Treating session values as shared globals
Injection Arrival or concurrency model Calling virtual users the same as requests per second
Assertion Automated acceptance threshold Reviewing charts without a release decision

Stable request names matter because the report groups measurements by those names. Use labels such as GET product detail or POST create order, not dynamic names that include a user ID. Dynamic values fragment the metrics into many tiny groups.

A scenario is not a test case in the UI-automation sense. It is a repeatable model of behavior. One virtual user may execute several requests, pauses, checks, and conditional branches. Therefore, ten users do not automatically equal ten requests. Estimate the requests generated by the actual scenario, then confirm with a small run.

Keep environment configuration external. Gatling provides parameters that a simulation can read, and your delivery platform can supply secrets through protected variables. Never commit production credentials or personal customer data. Test accounts should have controlled permissions and a documented reset strategy.

4. Write and Run Your First Gatling Simulation

Create src/basic-api.gatling.js with this runnable simulation. It targets Gatling's public demonstration API, starts one user, checks for HTTP 200, and applies strict smoke assertions. It is intentionally small so you can verify correctness before discussing capacity.

import {
  atOnceUsers,
  global,
  scenario,
  simulation
} from "@gatling.io/core";
import { http, status } from "@gatling.io/http";

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

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

  setUp(
    browseSession.injectOpen(atOnceUsers(1)).protocols(httpProtocol)
  ).assertions(
    global().failedRequests().count().is(0),
    global().responseTime().max().lt(3000)
  );
});

Run it from the project root:

npm run load:test

The command exits unsuccessfully if an assertion fails, which makes it useful in automation. Open the generated report under target/gatling after the run. Confirm that the request name, status, count, and timing look sensible.

The max response-time limit in this smoke example is not a universal service-level objective. Maxima are sensitive to one unusual sample, especially during longer tests. Production gates often use percentile requirements plus an error-rate limit, while maximum time remains diagnostic. Thresholds must come from product expectations and baseline evidence, not from numbers copied from a tutorial.

When adapting the example, change one variable at a time. First switch the base URL to an approved nonproduction environment. Next add authentication and a single business request. Then add checks. Only after a one-user run is correct should you add realistic arrival behavior. This sequence prevents a bad script from generating a large volume of misleading traffic.

5. Add Checks, Correlation, Session Data, and Feeders

A status code alone rarely proves business success. An API can return 200 with an empty result, an error object, or the wrong account. Gatling checks validate response content and can save a value into the virtual user's session for a later request. This is correlation: extracting a dynamic identifier or token rather than hard-coding it.

The exact check depends on the response type. For JSON APIs, use Gatling's supported JSONPath or JMESPath checks. For HTTP, an explicit status().is(200) makes the expectation visible even though Gatling also has an implicit successful-status check. Capture only values the scenario actually needs.

Feeders supply test data such as usernames, product IDs, or search terms. A CSV feeder lets the same scenario use many records. A simple file might be:

username,password
load_user_001,replace-from-secret-store
load_user_002,replace-from-secret-store

Do not store real passwords in the CSV. Generate the file securely for the run, or combine nonsecret identifiers from the feeder with a protected credential variable. Decide whether records can repeat. A queue-like feeder that runs out will fail virtual users, while a circular strategy repeats values and can create unrealistic contention. Match the strategy to account and data semantics.

Session data is isolated per virtual user. That makes it safe for a user-specific token, cart ID, or selected product. It does not eliminate server-side collisions if all users operate on the same account or order. Design unique data and cleanup boundaries just as you would for parallel functional tests. The test data management guide explains patterns for deterministic creation and cleanup.

Treat checks as guardrails for metrics. If authentication fails, stop or exit that user's dependent flow rather than sending a series of unauthorized requests. A fast chain of 401 responses can otherwise make latency look excellent while the business scenario is completely broken.

6. Gatling Basics for Testers: Choose the Right Workload Model

Gatling supports open and closed workload models. An open model controls arrivals over time. A closed model controls concurrent users in the system. Choose from the behavior being modeled, not from which API looks simpler.

Model Control variable Suitable example Main interpretation risk
Open New users per time unit Public shopping arrivals, API events Slow responses increase concurrency naturally
Closed Concurrent users Fixed worker pool or terminal population Throughput can fall when response time rises
At once Immediate starts Script smoke or synchronized spike experiment Creates an abrupt event that may be unrealistic
Ramp Gradual change Baseline and capacity discovery A short ramp may hide steady-state behavior
Constant rate Stable arrivals Service-level validation Requires enough duration for meaningful observation

An open profile can use rampUsers, constantUsersPerSec, or rampUsersPerSec. A closed profile can use constantConcurrentUsers or rampConcurrentUsers. Never mix open and closed injection steps in the same scenario injection definition.

This example ramps ten user starts over ten seconds, then introduces two new users per second for thirty seconds:

import {
  constantUsersPerSec,
  rampUsers
} from "@gatling.io/core";

const profile = browseSession.injectOpen(
  rampUsers(10).during(10),
  constantUsersPerSec(2).during(30)
);

An arrival rate is not concurrency. If users arrive faster than scenarios complete, active concurrency grows. Likewise, concurrency is not throughput because one virtual user may perform multiple operations and may wait between them. Use production telemetry or explicit business forecasts to derive the profile.

Duration matters. A brief run can catch gross errors but rarely reveals memory growth, connection exhaustion, background compaction, autoscaling behavior, or sustained queue buildup. Separate smoke, baseline, load, stress, spike, and endurance purposes. Each needs a different profile and stopping rule.

7. Model Real Behavior Without Copying Browser Noise

A useful performance scenario represents business work at the correct protocol boundary. Start from API contracts, server logs, browser network evidence, and product analytics. Identify essential requests, dependencies, think time, user mix, cache behavior, and data rules. Then remove analytics calls and third-party traffic that you do not own or lack permission to test.

Pauses are part of user behavior. Without them, a virtual user can loop far faster than a person and inflate request rate. At the same time, do not add arbitrary pauses merely to make a graph look comfortable. Use a documented distribution or a simple representative value when detailed evidence is unavailable, and label the assumption.

Model multiple journeys independently when they have different goals. A read-heavy browse scenario, a write-heavy checkout scenario, and an administrative export can have separate populations. This gives the report meaningful request groups and lets you tune traffic proportions. Avoid one mega-scenario that performs every action in the product.

Authentication deserves special design. If production users keep sessions for a long time, logging in on every loop overstates authentication traffic. If the requirement is specifically login capacity, isolate that transaction. Ensure tokens expire and refresh realistically when the duration requires it.

Gatling is not a browser rendering tool, so do not claim that a response-time percentile equals page experience. Protocol response time can be correlated with browser measurements, but rendering, client JavaScript, assets, and third-party dependencies add separate costs. Use the performance testing strategy guide to define which layer answers each risk.

Always obtain authorization for the target and load. Limit source IPs, schedule windows, and abort conditions. A technically correct simulation can still become an incident if it points at a shared environment without coordination.

8. Set Assertions That Produce a Defensible Verdict

Assertions make the simulation's exit status represent the performance contract. Without assertions, a report can be visually interesting but ambiguous in CI. Good assertions cover correctness and speed, and sometimes a critical request group separately from global behavior.

A practical assertion set often includes:

  • Global failed-request percentage or count.
  • A percentile response-time limit for the whole run.
  • Stricter requirements for a business-critical request.
  • Minimum request count when a missing workload would otherwise pass.
  • Different thresholds for smoke, baseline, and scheduled load suites.

Do not invent thresholds after viewing a failing result. Define them before the run using service objectives, customer expectations, known baselines, and environment differences. If a test environment has intentionally smaller capacity, document how its gate relates to production rather than pretending the numbers are identical.

Percentiles need sufficient samples. A p95 from twenty requests is a weak basis for a release decision. Inspect sample count and distribution. Averages can hide a slow tail, while p99 can be unstable with small volume. Use both summary metrics and time-series behavior.

Assertions also need scope. A permissive global percentile can hide a slow checkout if thousands of fast health requests dominate the run. Give business requests stable names and add request-level assertions when the critical path needs protection.

Keep infrastructure failures separate from product failures. DNS errors, generator CPU saturation, network loss, invalid test data, and application defects may all produce a failed run, but they require different owners. The automated verdict should stop a risky release. The diagnostic classification should explain what to investigate next.

9. Analyze the Report and Find the Bottleneck

Begin analysis with validity, not with latency. Confirm the correct build, target, data, request count, scenario mix, and injection profile. Check that the generator was healthy and that assertions executed. Then review failed requests and response codes before celebrating speed.

Gatling's local HTML report summarizes global and request-level response times, counts, errors, and active-user behavior. Use it to locate when degradation began and which request groups changed. A report tells you what the load generator observed. It does not identify a database lock, thread-pool queue, garbage collection pause, downstream timeout, or rate limiter by itself.

Correlate the run with application and infrastructure telemetry. Capture test start and end times, release identifier, environment, generator version, scenario commit, and workload parameters. Add a run annotation to monitoring if your platform supports it. Compare:

  • Request rate and active users from Gatling.
  • Service latency and error rate.
  • CPU, memory, garbage collection, threads, connections, and queues.
  • Database wait, slow query, pool, and lock metrics.
  • Cache hit rate and downstream dependency health.
  • Autoscaling and deployment events.

Look for the first constraint, not only the loudest late symptom. A database pool may saturate before application timeouts spike. A dependency may rate-limit before the main API returns errors. Compare with a known good baseline under the same controlled profile.

If the generator itself reaches CPU, network, file-descriptor, or memory limits, results are compromised. Distributed load generation can increase capacity, but it also adds coordination and network variables. Scale generators only after a controlled single-generator baseline.

10. Run Gatling Safely in CI and Maintain the Suite

CI is useful for script smoke tests and carefully sized performance checks. Large capacity or endurance tests often belong in a scheduled, isolated performance environment. A pull request pipeline should not flood a shared service every time a developer pushes.

A basic CI job performs a locked install, runs a named simulation with explicit parameters, and archives the Gatling result directory even on failure. Protect target URLs and credentials. Apply a job timeout that is longer than the planned profile but short enough to stop a hung run. Serialize jobs when the environment cannot support overlap.

Keep scenario code reviewable. Extract reusable protocol configuration and journey fragments only when reuse makes intent clearer. Version feeder schemas, describe required test accounts, and fail early when input data is missing. Store a short README beside the suite with the target approval process, expected duration, traffic estimate, abort rules, and owners.

Baseline management needs discipline. Compare like with like: same workload, environment capacity, application build class, data size, network path, and generator shape. Trend results rather than selecting a convenient prior run. When a threshold changes, review it as a requirement change and record the reason.

Schedule maintenance for the load suite. APIs change, authentication flows expire, data accumulates, and traffic mix evolves. A simulation that still compiles may no longer represent user behavior. Review scenarios with product and operations partners, not only with automation engineers.

Finally, use progressive execution: compile and lint, run one user, run a short low-rate check, then run the intended profile. That ladder catches cheap problems before expensive traffic begins.

Interview Questions and Answers

Q: What is Gatling, and how is it different from Selenium or Playwright?

Gatling is a protocol-level load-testing tool that models virtual-user traffic and records response behavior. Selenium and Playwright automate real browsers and validate user-facing behavior. I use Gatling for load, latency, errors, and capacity at the service boundary, then use a smaller browser suite for rendering and interaction risks.

Q: What is the difference between open and closed workload models?

An open model controls how frequently new users arrive, so concurrency can rise when the system slows. A closed model controls how many users are active, so throughput may fall when responses slow. I choose based on the real population behavior rather than converting one model into the other casually.

Q: Why are Gatling checks important in performance testing?

Checks prove that responses are functionally valid and can capture dynamic values for later requests. Without them, a fast error response might be counted as acceptable performance. I validate the minimum business condition and stop dependent actions when a prerequisite fails.

Q: How do you decide the number of virtual users?

I derive the profile from production telemetry, forecast arrivals, user concurrency, transaction mix, and scenario duration. Virtual users are not the same as requests per second. I start low, validate the model, and increase gradually while monitoring both the system and the generator.

Q: Which response-time statistic would you use for a gate?

I usually combine an error threshold with a percentile that reflects the service objective, plus request-specific limits for critical paths. Average latency alone can hide a slow tail. I also require enough samples and inspect the time series because a single summary can hide late-run degradation.

Q: How do you investigate a failed Gatling run?

I first validate target, build, data, load profile, sample count, checks, and generator health. Then I identify the earliest error or latency change and correlate it with application, database, dependency, and infrastructure telemetry. I classify the result as product, test, environment, or generator failure before assigning work.

Q: Should Gatling tests run on every pull request?

A low-load script validation can run on pull requests if the environment is safe. Large load, stress, and endurance tests usually run on a controlled schedule or release gate because they need isolated capacity and coordination. The CI command should still use assertions and archive evidence.

Common Mistakes

  • Sending load before a one-user scenario passes every check.
  • Treating virtual users, concurrent users, transactions, and requests per second as interchangeable.
  • Using a closed model for an arrival-driven system without explaining the choice.
  • Pointing a simulation at production or a shared environment without authorization.
  • Hard-coding tokens, customer data, hosts, or passwords in the simulation.
  • Giving every request a dynamic name, which fragments report metrics.
  • Checking only HTTP status when the body can contain a business error.
  • Replaying browser network noise, analytics, or third-party calls without purpose or permission.
  • Running with no pauses and claiming the traffic represents human behavior.
  • Selecting thresholds after seeing the result.
  • Trusting averages while ignoring percentiles, sample count, and time-series degradation.
  • Increasing generators before proving that the original generator is saturated.
  • Comparing runs with different data, builds, environments, or workload profiles.
  • Retrying failed requests invisibly and hiding service instability.
  • Publishing a chart without a clear pass, fail, and diagnostic classification.

Conclusion

Gatling basics for testers is not mainly about memorizing injection methods. It is about turning a business traffic question into a valid protocol scenario, a realistic workload, explicit checks, and predeclared assertions. The first trustworthy test is a one-user run that exercises the right path and fails for the right reason.

Create the small JavaScript project, run the demonstration simulation, and adapt one approved API journey. Add data and correlation, choose an evidence-based workload model, then increase traffic while correlating Gatling results with system telemetry. That sequence produces performance evidence a QA engineer can defend in a release discussion.

Interview Questions and Answers

Explain Gatling's simulation structure.

A simulation defines protocol configuration, one or more scenarios, workload injection, and assertions. Scenarios contain ordered actions such as HTTP requests, pauses, checks, and data handling. Injection controls when virtual users enter, while assertions determine the automated verdict.

How does a Gatling check improve test validity?

A check validates that the response represents business success, not merely a completed network call. It can also save a dynamic value into the virtual user's session for correlation. I use checks on prerequisites so invalid sessions do not generate misleading downstream traffic.

What is the difference between open and closed injection?

Open injection controls arrival rate and allows concurrency to grow when responses slow. Closed injection controls active concurrency, so throughput reacts to response time. I select the model from how demand enters the real system.

How would you create performance acceptance criteria?

I combine an allowed error rate with percentile response-time objectives and request-specific limits for critical transactions. I define thresholds before execution using service objectives and baseline evidence. I also require valid sample volume and workload completion.

Why can a load generator invalidate results?

If its CPU, memory, network, connection, or file limits saturate, it cannot produce or measure the intended traffic accurately. I monitor the generator during every serious run. I scale generation only after proving that it is the constraint.

How do you manage dynamic data in Gatling?

I use feeders for input records and session values for per-user state. Checks capture server-generated tokens or identifiers, which later requests reference. Test data must be isolated, secure, sufficient for the workload, and recoverable after partial failures.

What should a Gatling test report include for reproducibility?

I record the code commit, application build, environment, generator version, parameters, workload profile, time window, data version, assertions, and result artifacts. I also link relevant system telemetry. That context makes comparisons and incident analysis defensible.

Frequently Asked Questions

Is Gatling suitable for testers who do not know Scala?

Yes. Gatling provides JavaScript and TypeScript SDKs in addition to JVM language support. Testers can install the npm packages, write a .gatling.js or .gatling.ts simulation, and run it with the local CLI.

Does Gatling launch real browsers?

No. Gatling HTTP testing works at the protocol level and does not render pages or execute browser JavaScript. Pair it with Playwright or Selenium when client-side behavior and real browser experience must also be tested.

What is the difference between users and requests per second in Gatling?

A virtual user executes a scenario that may contain several requests and pauses. Requests per second is the resulting operation rate, not the user count. In an open workload, arrivals and scenario duration together influence active concurrency.

Should I use an open or closed Gatling workload model?

Use an open model when arrivals occur independently, such as shoppers or incoming API events. Use a closed model when the real system has a fixed active population, such as a bounded worker pool. Document the production behavior behind the choice.

How do I make a Gatling test fail in CI?

Add Gatling assertions for the required error and response-time limits. The run exits unsuccessfully when an assertion fails, allowing the CI job to block while retaining the report for diagnosis.

Where does the Gatling JavaScript CLI store local reports?

By default, local CLI runs write reports below target/gatling. The CLI supports configuration options for alternate source, resource, result, bundle, and package paths when the project needs a different layout.

Can I run Gatling against production?

Only with explicit authorization, safety limits, monitoring, rollback preparation, and an agreed window. Most teams begin in a production-like isolated environment because uncontrolled load can affect customers and shared dependencies.

Related Guides