Bundling Gatling Tests into an Executable JAR

Gatling is a fabulous framework for load and performance testing against web applications. Tests are written in Scala, and since Scala runs on the JVM, you can still use the Java libraries you know and love. And, if you are already using Maven to manage your project then look no further — the Gatling Maven plugin provides support for those of us wishing to continue using Maven for dependency management. This is especially good news if your tests rely on many libraries and you have no wish to duplicate work that’s already been done in an existing pom.xml.

Here is a great example of how to get the Gatling Maven plugin working. With it, the command to run a test looks something like this: 

mvn gatling:test -Dgatling.simulationClass=MySimulation

The only problem with this is it’s a Maven command. The machine running this needs to have Maven installed.

What if you wanted to run the tests somewhere else? I ran into this when I was trying to get a better understanding of a network latency issue. I wanted something a little more portable that I could put into a Docker container, something like a JAR.

The following guide is based on a Stack Overflow question. I have also uploaded a working sample to Github.

Prerequisites

Make sure you have the gatling-maven-plugin set up. It requires the gatling-charts-highcharts dependency,

<dependency>
<groupId>io.gatling.highcharts</groupId>
    <artifactId>gatling-charts-highcharts</artifactId>
    <version>3.0.0</version>
 </dependency>

and the plugin:

<build>
<plugins>
     <plugin>
         <groupId>io.gatling</groupId>
            <artifactId>gatling-maven-plugin</artifactId>
            <version>3.0.0</version>
       </plugin>
</plugins>
</build>

Step 1: The scala-maven-plugin

The first step is getting all the Scala stuff into the JAR. I find the scala-maven-plugin from net.alchim31.maven to be very straightforward and easy to integrate. I like it because I don’t have to explicitly declare dependencies like the Scala library and the Zinc compiler, it just does it for me.

        <plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<version>3.4.4</version>
<executions>
<execution>
<id>scala-compile-first</id>
<phase>process-resources</phase>
<goals>
<goal>add-source</goal>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>

By default, the scala-maven-plugin only looks in the src/main/scala directory. For that reason, I moved all my simulation classes to that directory and updated the gatling-maven-plugin with the new location. For good measure I also moved my gatling.conf file to src/main/resources.

<build>
<plugins>
     <plugin>
         <groupId>io.gatling</groupId>
            <artifactId>gatling-maven-plugin</artifactId>
            <version>3.0.0</version>
            <configuration>
                   <configFolder>src/main/resources</configFolder
                   <simulationsFolder>src/main/scala/gatling</simulationsFolder>
            </configuration>
        </plugin>
</plugins>
</build>

Step 2: The Uber JAR

The real magic happens when the maven-shade-plugin builds a shaded JAR with everything in it. This includes all of the libraries used in the tests, Gatling, and the Scala support needed to run it all. 

Set the mainClass property to io.gatling.app.Gatling

        <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>io.gatling.app.Gatling</mainClass>
</transformer>
</transformers>
<!-- Exclude manifest signature files. https://stackoverflow.com/questions/999489/invalid-signature-file-when-attempting-to-run-a-jar -->
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>

Step 3: Build the JAR!

Good old mvn clean install is all you need to build the jar. It will show up in the target directory alongside a separate folder of compiled test classes.

At this point, a command like this can be used to run a test:

java -cp JAR_NAME io.gatling.app.Gatling -s MySimulation

Extras

Run Script

A run script can make launching tests a lot easier. Something like this run.sh can be modified to handle user arguments that correspond to test parameters:

#!/bin/sh

USER_ARGS="-Dmy.test.parameter=$1"

JAR_LOCATION=find -L . -maxdepth 1 -name "*.jar" -type f -exec printf :{} ';'

java $USER_ARGS -cp $JAR_LOCATION io.gatling.app.Gatling -s MySimulation

Logback configuration

You can also add a logback.xml file to the project to configure logging.

<?xml version="1.0" encoding="UTF-8"?>
<configuration>

<!-- <appender name="FILE" class="ch.qos.logback.core.FileAppender"> -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- <file>../gatling.log</file> -->
<append>true</append>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%-5level] %logger{15} - %msg%n%rEx</pattern>
</encoder>
</appender>

<root level="info">
<appender-ref ref="STDOUT" />
</root>

</configuration>

Including run.sh, logback.xml, and additional resources as part of the build

In order to make use of the run.sh script or the logback.xml configuration file, you’ll want to include them in the build so they are bundled alongside the JAR. The <resources> section of the <build> configuration can be used for exactly this purpose.

    <resources>
<resource>
<filtering>true</filtering><directory>${project.basedir}/src/main/resources</directory>
<targetPath>${project.build.directory}</targetPath>
<includes>
<include>run.sh</include>
<include>logback.xml</include>
</includes>
</resource>
</resources>