javagatlinggatling-java

Gatling Java request to be called only once to retrieve login information for other requests that are executed multiple times


I want my login to be executed only once as it's managed by a third party service, so after the token is retrieved once for the whole duration of the tests, it should not execute again. That token should be injected as a cookie header for the other api calls, not changing regardless of how many calls are done to those other requests within the same running of the tests. Below is what I have, but maybe due to the way Gatling manages sessions, it's not working and the login is being executed as many times as the other api calls.

import io.gatling.javaapi.core.ScenarioBuilder;
import io.gatling.javaapi.core.Simulation;
import io.gatling.javaapi.http.HttpProtocolBuilder;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.atomic.AtomicReference;

import static io.gatling.javaapi.core.CoreDsl.*;
import static io.gatling.javaapi.http.HttpDsl.*;

@Slf4j
public class MySimulation extends Simulation {

    private static final String BASE_URL_DEV = "myurl";
    private static final String BASE_URL_STAGE = "myotherurl";
    private static final String ENV = System.getProperty("env");
    private static final String BASE_URL = ENV.equals("dev") ? BASE_URL_DEV : BASE_URL_STAGE;
    private static final String API_PATH = ENV.equals("dev") ? "" : "mypath";

    private static final AtomicReference<String> authCookieRef = new AtomicReference<>();

    private final HttpProtocolBuilder httpProtocol = http
            .disableFollowRedirect()
            .baseUrl(BASE_URL)
            .acceptHeader("application/json");

    private final ScenarioBuilder scn = scenario("MyScenario")
            .exec(session -> {
                if (authCookieRef.get() != null) {
                    System.out.println("User does not need cookie");
                    return session.set("authCookie", authCookieRef.get());
                }
                else {
                    System.out.println("User needs cookie");
                }
                return session.set("needLogin", true);
            })
            .doIf(session -> session.contains("needLogin")).then(
                    exec(http("Login")
                            .post("/login")
                            .header("Content-Type", "application/x-www-form-urlencoded")
                            .formParam("username", "username")
                            .formParam("password", "password")
                            .formParam("login-form-type", "token")
                            .check(status().is(302), header("Set-Cookie").saveAs("authCookie")))
                            .exec(session -> {
                                String authCookie = session.getString("authCookie").split(";")[0];
                                authCookieRef.set("auth_token=" + authCookie);
                                return session.set("authCookie", authCookieRef.get());
                            })
            )
            .exec(http("LastRefreshDate")
                    .get(API_PATH + "/last-refresh-date")
                    .header("cookie", "#{authCookie}")
                    .check(status().is(200))
            );

    {
        setUp(
                scn.injectOpen(atOnceUsers(2))
        ).protocols(httpProtocol);
    }
}

Thank you.

Update

Attempting to split the simulation into two different scenarios and doing my best guess on the Gatling syntax for retrieving the token based on my understanding from their docs.

package com.mycompany.performancetests;

import io.gatling.javaapi.core.ScenarioBuilder;
import io.gatling.javaapi.core.Simulation;
import io.gatling.javaapi.http.HttpProtocolBuilder;
import lombok.extern.slf4j.Slf4j;

import static io.gatling.javaapi.core.CoreDsl.atOnceUsers;
import static io.gatling.javaapi.core.CoreDsl.scenario;
import static io.gatling.javaapi.http.HttpDsl.*;

@Slf4j
public class MySimulation extends Simulation {

    private static final String BASE_URL_DEV = "myurl";
    private static final String BASE_URL_STAGE = "myanotherurl";
    private static final String ENV = System.getProperty("env", "dev");
    private static final String BASE_URL = ENV.equals("dev") ? BASE_URL_DEV : BASE_URL_STAGE;
    private static final String API_PATH = ENV.equals("dev") ? "" : "mypath";

    private final HttpProtocolBuilder httpProtocol = http
            .disableFollowRedirect()
            .baseUrl(BASE_URL)
            .acceptHeader("application/json");

    private final ScenarioBuilder loginScn = scenario("LoginScenario")
            .exec(http("Login")
                            .post("/login")
                            .header("Content-Type", "application/x-www-form-urlencoded")
                            .formParam("username", "username")
                            .formParam("password", "password")
                            .formParam("login-form-type", "token")
                            .check(status().is(302))
                    //      .check(mycompany("Set-Cookie").saveAs("auth_token"))
            )
            .exec(getCookieValue(CookieKey("auth_token")));

    private final ScenarioBuilder mainScn = scenario("MainScenario")
            .exec(addCookie(Cookie("auth_token", "value")))
            .exec(http("LastRefreshDate")
                    .get(API_PATH + "/last-refresh-date")
                    .check(status().is(200))
            )
            .exec(addCookie(Cookie("auth_token", "#{auth_token}")))
            .exec(http("Notifications")
                    .get(API_PATH + "/notifications")
                    .check(status().is(200))
            );

    {
        setUp(
                loginScn.injectOpen(atOnceUsers(1)).andThen(
                        mainScn.injectOpen(atOnceUsers(2))
                )
        ).protocols(httpProtocol);
    }
}

Solution

  • First, use Gatling's built-in tools for fetching and setting cookies: https://docs.gatling.io/reference/script/protocols/http/helpers/#dealing-with-cookies

    Then, my understanding is that you want to only perform login once and share the auth cookie for all the virtual users. You have to do 2 different scenarios:

    import io.gatling.javaapi.core.*;
    import io.gatling.javaapi.http.*;
    
    import static io.gatling.javaapi.core.CoreDsl.*;
    import static io.gatling.javaapi.http.HttpDsl.*;
    
    public class MySimulation extends Simulation {
    
      private static final String BASE_URL_DEV = "myurl";
      private static final String BASE_URL_STAGE = "myanotherurl";
      private static final String ENV = System.getProperty("env", "dev");
      private static final String BASE_URL = ENV.equals("dev") ? BASE_URL_DEV : BASE_URL_STAGE;
      private static final String API_PATH = ENV.equals("dev") ? "" : "mypath";
      private String cookie;
    
      private final HttpProtocolBuilder httpProtocol = http
          .baseUrl(BASE_URL)
          .acceptHeader("application/json");
    
      private final ScenarioBuilder loginScn = scenario("LoginScenario")
          .exec(
              http("Login")
                  .post("/login")
                  .formParam("username", "username")
                  .formParam("password", "password")
                  .formParam("login-form-type", "token")
                  .asFormUrlEncoded(),
              getCookieValue(CookieKey("auth_token")),
              exec(session -> {
                cookie = session.getString("auth_token");
                return session;
              })
          );
    
      private final ScenarioBuilder mainScn = scenario("MainScenario")
          .exec(
              addCookie(Cookie("auth_token", session -> cookie)),
              http("LastRefreshDate")
                  .get(API_PATH + "/last-refresh-date")
                  .check(status().is(200)),
              http("Notifications")
                  .get(API_PATH + "/notifications")
                  .check(status().is(200))
          );
    
      {
        setUp(
            loginScn.injectOpen(atOnceUsers(1)).andThen(
                mainScn.injectOpen(atOnceUsers(2))
            )
        ).protocols(httpProtocol);
      }
    }