Load Testing Using Gatling and Java # 5 : Loops and Conditions

In this blog post, we’ll explore how to use loops and conditional statements in Gatling to improve the flow and flexibility of your performance testing scriptsThe complete code can be found at the GitHub repo.

Why do Loops and Conditionals Matter in Load Testing?

   Loops: In real-world scenarios, users interact with web applications repetitively browsing through multiple pages, making repeated API calls, or performing continuous actions within the application. Using loops in Gatling helps simulate such behavior.

   Conditional Statements: Not all users behave the same way, and sometimes you may want to perform different actions based on specific conditions—such as whether a certain request was successful or based on the user's profile or session data. Conditional logic allows you to introduce this variability into your scripts.

Now, let's look at how to implement loops and conditional statements in Gatling.

1. Loops in Gatling

Gatling provides several loops to simulate repetitive user behavior. Let's dive into the main types of loops you can implement in your load testing scripts:

a) repeat Loop

The repeat loop repeats a block of code a specific number of times. This is useful when you want to simulate repetitive user behavior, such as submitting forms or making API requests multiple times.

ScenarioBuilder repeat = scenario("Repeat loop")
.exec(repeat(5).on(exec(carsClient.getCarByBrand("tata"))));

Above code will repeat the call to  getCar API 5 times.

Repeat loop takes 2 params:

1. times: number of times to repeat the loop

2. counterName: optional, key to the loop counter as stored in session

b) forEach Loop

forEach loop allows you to iterate over a list and perform an operation on each element.

ScenarioBuilder forEach = scenario("For Each loop")
.exec(foreach(List.of("tata", "ford", "maruti"), "brand")
.on(carsClient.getCarByBrand("#{brand}")));

forEach loop takes 3 parameters:

  1. the list of elements to iterate over
  2. elementName: the key to the current element in the Session
  3. counterName: optional, key to the loop counter as stored in session

 API sample: http://localhost:3000/cars?brand=Tata

Here getCarByBrand API will be executed three times with different query params each time tata, ford and maruti.

c) during Loop

The during loop is used for iteration over a given duration of time. During loop accepts three parameters:

  1. duration
  2. counterName: optional, key to the loop counter as stored in session
  3. exitAsap: Optional, default is true

The last parameter, exitASAP, defaults to true, meaning the loop will terminate immediately once the specified duration is reached, even if there are remaining actions to complete the current iteration. This could be problematic if subsequent actions depend on the execution of all requests within the loop.

ScenarioBuilder during = scenario("During loop")
.exec(during(Duration.ofSeconds(2))
.on(exec(carsClient.getCarByBrand("tata"))));

In the above example during loop will exit as soon as 2 seconds are over.

ScenarioBuilder duringExitAsap = scenario("During loop exit asap")
.exec(during(Duration.ofSeconds(2), false)
.on(exec(carsClient.getCarByBrand("tata"))));

In the above example exitAsap is set to false which means that all the actions inside during loop will be executed even if the 2 seconds have passed.

d) asLongAs Loop

The asLongAs loop is used to execute a block of actions repeatedly while a condition remains true, much like a while loop in many programming languages. The condition is evaluated at the start of each iteration.

asLongAs loop takes 3 parameters:

  1. condition: boolean, gatling EL resolving to boolean or a function
  2. counterName: optional, key to the loop counter as stored
  3. exitAsap: Optional, default is true
ScenarioBuilder asLongAs = scenario("As long as loop")
.exec(carsClient.getCarById("1"))
.exec(session -> carsClient.setCondition(session))
.exec(asLongAs("#{condition}", "counter")
.on(exec(carsClient.getCarByBrand("tata")
.exec(session -> {
if (session.getInt("counter") == 5) {
return session.set("condition", false);
}
return session;
})
)));
  1. Executes a request to get a car by its ID (getCarById("1")).
  2. Sets a condition in the session based on the car's brand using the setCondition method.
  3. Enters an asLongAs loop that continues as long as the condition session variable is true.
  4. Inside the loop, it executes a request to get a car by the brand "tata".
  5. Checks if the counter session variable equals 5, and if so, sets the condition session variable to false, breaking the loop.

e) doWhile Loop

The doWhile loop is used to repeatedly execute a set of actions at least once and continue executing them while a specific condition remains true. Unlike a aslongas loop, a doWhile loop always runs the actions at least once, even if the condition is false initially, because the condition is checked after each iteration.

This loop is useful when you need to execute an action once and then repeat it based on a condition being true after the action is executed.

doWhile loop takes 2 parameters:

  1. condition: boolean, gatling EL resolving to boolean or a function
  2. counterName: optional, key to the loop counter as stored.
ScenarioBuilder doWhile = scenario("do while loop")
.exec(session -> session.set("condition", false))
.exec(doWhile("#{condition}", "counter")
.on(exec(carsClient.getCarByBrand("tata")
.exec(session -> {
if (session.getInt("counter") == 5) {
return session.set("condition", false);
}
return session;
})
)));
  1. Sets the condition session variable to true.
  2. Enters a doWhile loop that continues as long as the condition session variable is true.
  3. Inside the loop, it executes a request to get a car by the brand "tata".
  4. Checks if the counter session variable equals 5, and if so, sets the condition session variable to false, breaking the loop.

f) asLongAsDuring Loop

The asLongAsDuring loop is a combination of two concepts: it allows you to repeatedly execute a block of actions while a condition is true for a specified duration. This means that the loop continues executing the actions as long as the condition holds true, but it will stop after the defined duration, even if the condition hasn't been falsified yet.

asLongAsDuring loop takes 4 parameters:

  1. condition: boolean, gatling EL resolving to boolean or a function
  2. counterName: optional, key to the loop counter as stored
  3. exitAsap: Optional, default is true
  4. duration
ScenarioBuilder asLongAsDuring = scenario("as long as during")
.exec(session -> session.set("condition", true))
.exec(asLongAsDuring("#{condition}", 3)
.on(exec(carsClient.getCarByBrand("tata")
)));
  1. Sets the condition session variable to true.
  2. Enters an asLongAsDuring loop that continues as long as the condition session variable is true and for a maximum of 3 seconds.
  3. Inside the loop, it executes a request to get a car by the brand "tata".
  4. If the condition session variable is set to false before 3 seconds, the loop will exit early.

g) doWhileDuring Loop

The doWhileDuring loop is a combination of two concepts: it allows you to execute a set of actions at least once and continue executing those actions while a condition is true, but it also imposes a time duration limit. The loop will keep executing the actions as long as the condition is true and the total duration hasn't passed.

doWhileDuring loop takes 3 parameters:

  1. condition: boolean, gatling EL resolving to boolean or a function
  2. counterName: optional, key to the loop counter as stored
  3. Duration
ScenarioBuilder doWhileDuring = scenario("do while during")
.exec(session -> session.set("condition", true))
.exec(doWhileDuring("#{condition}", 3)
.on(exec(carsClient.getCarByBrand("tata")
)));

h) forever Loop

The forever loop is used to repeatedly execute a block of actions indefinitely (or until the test is stopped). It’s useful when you want to repeat a set of actions without any predefined condition for exit, effectively creating an infinite loop. This type of loop is often used in load testing scenarios where you want to simulate continuous traffic, such as persistent users who keep making requests without stopping.

ScenarioBuilder forever = scenario("forever")
.exec(forever().on(exec(carsClient.getCarByBrand("tata")
)));

2. Conditional Statements

a)  doIf

The doIf construct allows you to execute a block of actions conditionally. It’s essentially an if-statement for your load simulation, meaning you can specify an expression, and if that expression evaluates to true, the actions inside the doIf block are executed.

ScenarioBuilder doIf = scenario("do if condition")
.exec(session -> session.set("condition", false))
.exec(doIf("#{condition}")
.then(exec(carsClient.getCarById("1"))))
.exec(session -> session.set("condition", true))
.exec(doIf("#{condition}")
.then(exec(carsClient.getCarById("1"))));

It performs the following steps:

  1. Sets the condition session variable to false.
  2. Executes a doIf block that checks the condition session variable. Since condition is false, the request to get a car by ID (getCarById("1")) is not executed.
  3. Sets the condition session variable to true.
  4. Executes another doIf block that checks the condition session variable. Since condition is true, the request to get a car by ID (getCarById("1")) is executed.

b) doIfEquals

The doIfEquals construct is used to execute a block of actions only if a given session attribute equals a specific value. It's a convenient way to conditionally execute actions based on exact matches of session variables.

ScenarioBuilder doIfEquals = scenario("do if equals condition")
.exec(session -> session.set("brand", "tata"))
.exec(doIfEquals("#{brand}", "tata")
.then(exec(carsClient.getCarById("1"))))
.exec(doIfEquals("#{brand}", "ford")
.then(exec(carsClient.getCarById("1"))));

 The code defines a Gatling scenario named "do if equals condition". It performs the following steps:

1. Sets the `brand` session variable to `"tata"`.

2. Executes a `doIfEquals` block that checks if the `brand` session variable equals `"tata"`. If true, it executes a request to get a car by ID (`getCarById("1")`).

3. Executes another `doIfEquals` block that checks if the `brand` session variable equals `"ford"`. Since session variable is tata the doIfEquals block will not execute.

c) doIfOrElse

The doIfElse is a control structure used to conditionally execute a block of code depending on a specified condition. It's a way to perform different actions in your Gatling simulations based on whether the condition evaluates to true or false.

ScenarioBuilder doIfOrElse = scenario("do if or else condition")
.exec(session -> session.set("condition", true))
.exec(doIfOrElse("#{condition}")
.then(exec(carsClient.getCarById("1")))
.orElse(exec(carsClient.getCarById("2"))));
  1. Sets the condition session variable to true.
  2. Executes a doIfOrElse block that checks the condition session variable.
  3. If condition is true, it executes a request to get a car by ID (getCarById("1")).
  4. If condition is false, it executes a request to get a car by ID (getCarById("2")). 

d) doIfEqualsOrElse

The doIfEqualsOrElse is a control structure that allows you to perform actions based on whether a value is equal to a specific value. If the value equals the specified one, it executes a certain block of code; otherwise, it executes a different block of code.

ScenarioBuilder doIfEqualsOrElse = scenario("do if equals or else condition")
.exec(session -> session.set("brand", "tata"))
.exec(doIfEqualsOrElse("#{brand}", "tata")
.then(exec(carsClient.getCarById("1")))
.orElse(exec(carsClient.getCarById("2"))));

e) doSwitch

doSwitch is a control structure that allows you to perform different actions based on a condition that is evaluated at runtime. This is useful when you want to dynamically select between multiple alternative actions or workflows in your test scenario.

ScenarioBuilder doSwitch = scenario("do switch condition")
.exec(session -> session.set("brand", "#{brand}"))
.exec(doSwitch("#{brand}").on(
onCase("tata").then(exec(carsClient.getCarByBrand("tata"))),
onCase("maruti").then(exec(carsClient.getCarByBrand("maruti"))),
onCase("ford").then(exec(carsClient.getCarByBrand("ford")))
));

f) doSwitchOrElse

doSwitchOrElse is a control structure that allows you to perform different actions based on a value evaluated at runtime. It's similar to doSwitch, but it provides an else block that is executed if none of the specified cases match. This is useful when you have multiple possible values to check and a default action to take if no cases match.

ScenarioBuilder doSwitchOrElse = scenario("do switch or else condition")
.exec(session -> session.set("brand", "#{brand}"))
.exec(doSwitchOrElse("#{brand}").on(
onCase("tata").then(exec(carsClient.getCarByBrand("tata"))),
onCase("maruti").then(exec(carsClient.getCarByBrand("maruti"))),
onCase("ford").then(exec(carsClient.getCarByBrand("ford")))
).orElse(
exec(carsClient.getCarByBrand("tata"))
));

g) randomSwitch

The randomSwitch is a control structure that allows you to randomly choose between multiple actions.randomSwitch can be used to emulate simple Markov chains. A Markov Chain is a powerful tool for modeling random processes where the future depends only on the present state, not the history of how the system arrived at that state. Percentages sum can’t exceed 100%.

ScenarioBuilder randomSwitch = scenario("random switch condition")
.exec(randomSwitch().on(
percent(60.0).then(exec(carsClient.getCarByBrand("tata"))),
percent(20.0).then(exec(carsClient.getCarByBrand("maruti"))),
percent(20.0).then(exec(carsClient.getCarByBrand("ford")))
));

 randomSwitch block to execute different requests based on specified probabilities:

  1. 60% chance to execute a request to get a car by brand "tata".
  2. 20% chance to execute a request to get a car by brand "maruti".
  3. 20% chance to execute a request to get a car by brand "ford".

h) randomSwitchOrElse

It's similar to randomSwitch, but it provides an else block that is executed if none of the specified cases match.

ScenarioBuilder randomSwitchOrElse = scenario("random switch or Else condition")
.exec(randomSwitchOrElse().on(
percent(60.0).then(exec(carsClient.getCarByBrand("tata"))),
percent(20.0).then(exec(carsClient.getCarByBrand("maruti"))),
percent(20.0).then(exec(carsClient.getCarByBrand("ford")))
).orElse(
exec(carsClient.getCarByBrand("tata"))
));

i) uniformRandomSwitch

uniformRandomSwitch is a control structure that allows you to randomly select one action from a set of alternatives, with equal probability for each action. This means that each action has the same chance of being selected, as opposed to the randomSwitch, where you can specify different probabilities (weights) for each action.

The uniformRandomSwitch is particularly useful when you want to simulate a truly random selection between different actions, without assigning specific probabilities or weights.

ScenarioBuilder uniformRandomSwitch = scenario("uniform random switch")
.exec(uniformRandomSwitch().on(
exec(carsClient.getCarByBrand("tata")),
exec(carsClient.getCarByBrand("maruti")),
exec(carsClient.getCarByBrand("ford"))
));

 uniformRandomSwitch randomly selects one of the three cases, each with an equal probability of 33.33%.

j) roundRobinSwitch

roundRobinSwitch is a control structure that allows you to cycle through a set of actions in a round-robin fashion. This means it will execute each action in turn, one after the other, repeatedly cycling through the list of action.

ScenarioBuilder roundRobinSwitch = scenario("round robin switch")
.exec(roundRobinSwitch().on(
exec(carsClient.getCarByBrand("tata")),
exec(carsClient.getCarByBrand("maruti")),
exec(carsClient.getCarByBrand("ford"))
));

 First, it executes a request to get a car by brand "tata".

Next, it executes a request to get a car by brand "maruti".

And third times, it executes a request to get a car by brand "ford" and from fourth it executes a request to get a car by brand "tata" and the cycle goes on.

We can use loops and conditional statements together to make the scenarios more realistic. For example:

ScenarioBuilder doSwitchAndRepeat = scenario("Do switch and Repeat loop")
.exec(doSwitch("#{brand}").on(
onCase("tata").then(repeat(2).on(
exec(carsClient.getCarByBrand("tata")))),
onCase("maruti").then(repeat(3).on(
exec(carsClient.getCarByBrand("maruti")))),
onCase("ford").then(repeat(4).on(
exec(carsClient.getCarByBrand("ford")))
)));

That is all for Loops and Conditions, in next article we will explore more features of gatling. Till then, Happy Load Testing! 🚀

Post a Comment

Previous Post Next Post