Simulation

Learn about the main parts of a Gatling simulation: DSL imports, scenario definitions, simulation definitions, hooks

Simulation is the parent class your tests must extend so Gatling can launch them.

DSL imports

The Gatling DSL requires some imports:

     
// required for Gatling core structure DSL
import io.gatling.javaapi.core.*;
import static io.gatling.javaapi.core.CoreDsl.*;

// required for Gatling HTTP DSL
import io.gatling.javaapi.http.*;
import static io.gatling.javaapi.http.HttpDsl.*;

// can be omitted if you don't use jdbcFeeder
import io.gatling.javaapi.jdbc.*;
import static io.gatling.javaapi.jdbc.JdbcDsl.*;

// used for specifying durations with a unit, eg Duration.ofMinutes(5)
import java.time.Duration;
// required for Gatling core structure DSL
import io.gatling.javaapi.core.*
import io.gatling.javaapi.core.CoreDsl.*

// required for Gatling HTTP DSL
import io.gatling.javaapi.http.*
import io.gatling.javaapi.http.HttpDsl.*

// can be omitted if you don't use jdbcFeeder
import io.gatling.javaapi.jdbc.*
import io.gatling.javaapi.jdbc.JdbcDsl.*

// used for specifying durations with a unit, eg Duration.ofMinutes(5)
import java.time.Duration
// required for Gatling core structure DSL
import io.gatling.core.Predef._

// required for Gatling HTTP DSL
import io.gatling.http.Predef._

// can be omitted if you don't use jdbcFeeder
import io.gatling.jdbc.Predef._

// used for specifying durations with a unit, eg "5 minutes"
import scala.concurrent.duration._

setUp

Most pieces of your tests can possibly be extracted into other helper classes so you can bring your own test libraries: scenarios, protocols, headers, injection profiles, etc.

The only mandatory piece in your Simulations is that they must call the setUp method exactly once in their constructor to register the test components.

     
ScenarioBuilder scn = scenario("scn"); // etc...

setUp(
  scn.injectOpen(atOnceUsers(1))
);
val scn = scenario("scn") // etc...

setUp(
  scn.injectOpen(atOnceUsers(1))
)
val scn = scenario("scn") // etc...

setUp(
  scn.inject(atOnceUsers(1))
)

which correspond to injecting one single user into the scn scenario.

It’s possible to have multiple populations, ie scenarios with an associated injection profile, in the same simulation:

     
ScenarioBuilder scn1 = scenario("scn1"); // etc...
ScenarioBuilder scn2 = scenario("scn2"); // etc...

setUp(
  scn1.injectOpen(atOnceUsers(1)),
  scn2.injectOpen(atOnceUsers(1))
);
val scn1 = scenario("scn1") // etc...
val scn2 = scenario("scn2") // etc...

setUp(
  scn1.injectOpen(atOnceUsers(1)),
  scn2.injectOpen(atOnceUsers(1))
)
val scn1 = scenario("scn1") // etc...
val scn2 = scenario("scn2") // etc...

setUp(
  scn1.inject(atOnceUsers(1)),
  scn2.inject(atOnceUsers(1))
);

For more information regarding scenarios, see the dedicated section here.

For more information regarding injection profiles, see the dedicated section here.

Protocols Configuration

Protocols configurations can be attached

  • either on the setUp, in which case they are applied on all the populations
  • or on each population, so they can have different configurations
     
// HttpProtocol configured globally
setUp(
  scn1.injectOpen(atOnceUsers(1)),
  scn2.injectOpen(atOnceUsers(1))
).protocols(httpProtocol);

// different HttpProtocols configured on each population
setUp(
  scn1.injectOpen(atOnceUsers(1))
    .protocols(httpProtocol1),
  scn2.injectOpen(atOnceUsers(1))
    .protocols(httpProtocol2)
);
// HttpProtocol configured globally
setUp(
  scn1.injectOpen(atOnceUsers(1)),
  scn2.injectOpen(atOnceUsers(1))
).protocols(httpProtocol)

// different HttpProtocols configured on each population
setUp(
  scn1.injectOpen(atOnceUsers(1))
    .protocols(httpProtocol1),
  scn2.injectOpen(atOnceUsers(1))
    .protocols(httpProtocol2)
)
// HttpProtocol configured globally
setUp(
  scn1.inject(atOnceUsers(1)),
  scn2.inject(atOnceUsers(1))
).protocols(httpProtocol)

// different HttpProtocols configured on each population
setUp(
  scn1.inject(atOnceUsers(1))
    .protocols(httpProtocol1),
  scn2.inject(atOnceUsers(1))
    .protocols(httpProtocol2)
)

For more information regarding protocols configurations, see the HttpProtocol section here.

Acceptance Criteria

Assertions are configured on the setUp.

     
setUp(scn.injectOpen(atOnceUsers(1)))
  .assertions(global().failedRequests().count().is(0L));
setUp(scn.injectOpen(atOnceUsers(1)))
  .assertions(global().failedRequests().count().shouldBe(0L))
setUp(scn.inject(atOnceUsers(1)))
  .assertions(global.failedRequests.count.is(0))

For more information regarding assertions, see the dedicated section here.

Global Pause configuration

The pauses can be configured on Simulation with a bunch of methods:

     
// pause configuration configured globally
setUp(scn.injectOpen(atOnceUsers(1)))
  // disable the pauses for the simulation
  .disablePauses()
  // the duration of each pause is what's specified
  // in the `pause(duration)` element.
  .constantPauses()
  // the duration of each pause is on average that specified
  // in the `pause(duration)` element and follows a uniform distribution.
  .uniformPauses(0.5)
  .uniformPauses(Duration.ofSeconds(2))
  // the duration of each pause is on average that specified
  // in the `pause(duration)` element and follows an exponential distribution.
  .exponentialPauses()
  // the pause duration is computed by the provided function (in milliseconds).
  // In this case the filled duration is bypassed.
  .customPauses(session -> 5L);

// different pause configurations configured on each population
setUp(
  scn1.injectOpen(atOnceUsers(1))
    .disablePauses(),
  scn2.injectOpen(atOnceUsers(1))
    .exponentialPauses()
);
// pause configuration configured globally
setUp(scn.injectOpen(atOnceUsers(1)))
  // disable the pauses for the simulation
  .disablePauses()
  // the duration of each pause is what's specified
  // in the `pause(duration)` element.
  .constantPauses()
  // the duration of each pause is on average that specified
  // in the `pause(duration)` element and follows a uniform distribution.
  .uniformPauses(0.5)
  .uniformPauses(Duration.ofSeconds(2))
  // the duration of each pause is on average that specified
  // in the `pause(duration)` element and follows an exponential distribution.
  .exponentialPauses()
  // the pause duration is computed by the provided function (in milliseconds).
  // In this case the filled duration is bypassed.
  .customPauses { session -> 5L }

// different pause configurations configured on each population
setUp(
  scn1.injectOpen(atOnceUsers(1))
    .disablePauses(),
  scn2.injectOpen(atOnceUsers(1))
    .exponentialPauses()
)
// pause configuration configured globally
setUp(scn.inject(atOnceUsers(1)))
  // disable the pauses for the simulation
  .disablePauses
  // the duration of each pause is what's specified
  // in the `pause(duration)` element.
  .constantPauses
  // the duration of each pause is on average that specified
  // in the `pause(duration)` element and follows a uniform distribution.
  .uniformPauses(0.5)
  .uniformPauses(2.seconds)
  // the duration of each pause is on average that specified
  // in the `pause(duration)` element and follows an exponential distribution.
  .exponentialPauses
  // the pause duration is computed by the provided function (in milliseconds).
  // In this case the filled duration is bypassed.
  .customPauses(session => 5L)

// different pause configurations configured on each population
setUp(
  scn1.inject(atOnceUsers(1))
    .disablePauses,
  scn2.inject(atOnceUsers(1))
    .exponentialPauses
)

Shaping Throughput

Some users might want to reason in terms of throughput/requests per second instead of virtual users.

If your virtual users perform only one request each, you should use an open workload model for this, such as constantUsersPerSec.

Otherwise, in our opinion, you’re going to run into trouble because you won’t have any means of controlling which request gets executed.

Still, you can try and use the throttle method, that can be defined either globally or per scenario.

What throttle do is that it sets an upper limit on your throughput that would normally be generated with the scenarios and the injection profiles you’ve defined so that you can shape it the way you want. It’s only capable of reducing the normal throughput, not increase it.

Throttling is currently only supported for HTTP requests and JMS.

     
// throttling profile configured globally
setUp(scn.injectOpen(constantUsersPerSec(100).during(Duration.ofMinutes(30))))
  .throttle(
    reachRps(100).in(10),
    holdFor(Duration.ofMinutes(1)),
    jumpToRps(50),
    holdFor(Duration.ofHours(2))
  );

// different throttling profiles configured globally
setUp(
  scn1.injectOpen(atOnceUsers(1))
    .throttle(reachRps(100).in(10)),
  scn2.injectOpen(atOnceUsers(1))
    .throttle(reachRps(20).in(10))
);
// throttling profile configured globally
setUp(scn.injectOpen(constantUsersPerSec(100.0).during(Duration.ofMinutes(30))))
  .throttle(
    reachRps(100).during(10),
    holdFor(Duration.ofMinutes(1)),
    jumpToRps(50),
    holdFor(Duration.ofHours(2))
  )

// different throttling profiles configured globally
setUp(
  scn1.injectOpen(atOnceUsers(1))
    .throttle(reachRps(100).during(10)),
  scn2.injectOpen(atOnceUsers(1))
    .throttle(reachRps(20).during(10))
)
// throttling profile configured globally
setUp(scn.inject(constantUsersPerSec(100).during(30.minutes)))
  .throttle(
    reachRps(100).in(10),
    holdFor(1.minute),
    jumpToRps(50),
    holdFor(2.hours)
  )

// different throttling profiles configured globally
setUp(
  scn1.inject(atOnceUsers(1))
    .throttle(reachRps(100).in(10)),
  scn2.inject(atOnceUsers(1))
    .throttle(reachRps(20).in(10))
)

This simulation will reach 100 req/s with a ramp of 10 seconds, then hold this throughput for 1 minute, jump to 50 req/s and finally hold this throughput for 2 hours.

The building blocks for throttling are:

  • reachRps(target).in(duration): target a throughput with a ramp over a given duration.
  • jumpToRps(target): jump immediately to a given targeted throughput.
  • holdFor(duration): hold the current throughput for a given duration.

Maximum Duration

Finally, with maxDuration you can force your run to terminate based on a duration limit, even if some virtual users are still running.

It is useful if you need to bound the duration of your simulation when you can’t predict it.

     
setUp(scn.injectOpen(rampUsers(1000).during(Duration.ofMinutes(20))))
  .maxDuration(Duration.ofMinutes(10));
setUp(scn.injectOpen(rampUsers(1000).during(Duration.ofMinutes(20))))
  .maxDuration(Duration.ofMinutes(10))
setUp(scn.inject(rampUsers(1000).during(20.minutes)))
  .maxDuration(10.minutes)

Hooks

Gatling provides two hooks:

  • before for executing some arbitrary code before the simulation actually runs
  • after for executing some arbitrary code after the simulation actually runs

The lifecycle is as follows:

  1. Gatling starts
  2. Simulation constructor is called and all the code in the class body not delayed in before and after hooks is executed
  3. before hook is executed
  4. Simulation runs
  5. Simulation terminates
  6. after hook is executed
  7. HTML reports are generated if enabled
  8. Gatling shuts down
     
@Override
public void before() {
  System.out.println("Simulation is about to start!");
}

@Override
public void after() {
  System.out.println("Simulation is finished!");
}
override fun before() {
  println("Simulation is about to start!")
}

override fun after() {
  println("Simulation is finished!")
}
before {
  println("Simulation is about to start!")
}

after {
  println("Simulation is finished!")
}

Edit this page on GitHub