Gatling-sbt - Running gatling tests from a classpath/jar file

Hi There,

I have a project А that contains a Gatling simulation in compile scope. The project’s jar file is published to an Ivy repository.

There is a Gatling enabled sbt project B that has a reference to the artifact of A.

  lazy val projectA = Project(name, file(s"B"))
      .enablePlugins(GatlingPlugin)
      .settings(libraryDependencies ++= 
         Seq(
          "io.gatling.highcharts" % "gatling-charts-highcharts" % "2.0.0-SNAPSHOT" % "test",
          "io.gatling" % "test-framework" % "1.0-SNAPSHOT" % "test",
          "project.a" % "A" % "1.0.0-SNAPSHOT" % "test
         ))
      .settings(resolvers += "Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots")

  lazy val testGatling = taskKey[Unit]("Runs the gatling test")
  lazy val testGatlingTask = testGatling := {
    (test in projectA in Gatling).value
  }

I would like to execute the Gatling simulations defined in A from the B sbt build.

Nothing is run when I run the testGaling task. When I debug I can see that:

  • (definedTests in gatlingTests in Gatling) is empty

  • (testGrouping in gatlingTests in Gatling) is empty

How can I make Gatilng recognise Secanious defined in the class path of the project?

Many thanks,
Lucho

Hi Lucho,

By any chance, could it be that you missed to set publishArtifact in Test := true on project A ?
Even then, I have doubts that SBT testing infrastructure is able to pick up tests from JARs…

Cheers,

Pierre

Hi Pierre,

Thanks for the hint!
Project A is definitely published to my local ivy cache and to the nexus ivy repository and I can see it in the class path of project B as a resolved dep.

I wonder how is sbt recognising Gatling Simulations as tests? Does it analyse the code base or it scans the class path? If it scans the classpath there might be a way to reuse Integration tests and Stress (Gatling) in different projects. My use case is running the same suite of Gatling tests against development application and then again in the staging environment after all the deployment has finished in staging environment (which is very very much like the production environment).

I don’t insist in running the Gatling tests with the gatling-sbt plugin. Would there be a way to run the Gatling.main within a classpath that contains a bunch of simulations ? If there is I can easilly compose the appropriate set of deps to run the main method against. I can do that either in a separate JVM or in a separate classloader.

I just need a way to execute the Gatling.main with the correct arguments? Do you have any hints about that?

Thanks,
Lucho

The process by which SBT identifies some classes as tests to be run is in fact pretty simple :

  1. SBT has the notion of test frameworks, which defines which classes are to be considered as tests, and how to run them.
  2. The SBT plugin comes with Gatling’s own test framework, which defines that classes extending Simulation are to be picked up by the test framework in order to be run.
  3. SBT takes the result of tests classes compilation, matches them with the defined test frameworks, before executing them with the appropriate test framework.

But as you can see in point 3) , it relies on the result of test classes compilation, meaning that tests classes from JARs can’t be picked up.

I don’t know about the exact structure of your project and if it may be not possible to implement it nor desirable, but one way to do it would be to :

  1. defines two custom SBT tasks in project A, one for running Gatling on the dev environment, one for for running Gatling on the staging environnement. those tasks would both simply call (test in Gatling).value. Wait for 3) to see why they would be useful :wink:
  2. Modify the simulations so that they could be parametrized for each environnement : one such example would be the URL that the simulation will hit, as they are surely not the same for each environnement.
  3. Pass those parameters to Gatling using System properties, which can be configured by the javaOptions setting, e.g. javaOptions in Test := Seq("-Dsimulation.url=http://www.mywebsite.com”). Of course those System properties will have to differ for each task, but luckily SBT allows to scope settings/tasks by task. You would then have something like that : javaOptions in (Test, runGatlingOnStaging) := Seq("-Dsimulation.url=http://www.mywebsite.com”)
  4. You can then selectively run Gatling with the proper parameters for each environment using the customs tasks.

You can also run simulations in JARs using the bundle, if you put the JAR in gatling.sh in the bundle lib/ folder and disable the compiler in gatling.conf.
You can then specify which simulation to run using the -s option.

Hope this helps,

Pierre

Hi Pierre,

That actually helps a lot :slight_smile: thanks! It is useful in a way that it verifies my suspicions and invalidates one of the directions I had as an option. The direction is running the Gatling simulations using the gatling-sbt plugin.

I am very aware for the second option. This is our current situation. We run the application manually which is a set of many services (each one run manually) and then we point Gatling test run to our local application using a system property. This requires few manual steps which I am currently automating. These are running 4+ services and a web application that are run manually and then running Gatling against the app. These should all be doable with a single sbt command.

I will focus my attempts in composing a classpath that contains the Gatling runner and my jar file with simulations. Then I hope to be able to run the Gatling.main with compilation disabled as you suggested. I am doing a similar thing for different main methods in order to setup a sandbox with all the services and the web application anyway. I guess this is all googleable :slight_smile:

Hope this works and thanks again!
Lucho

I manage to do it like that:

lazy val testGatling = taskKey[Unit]("Runs the gatling test")
  lazy val testGatlingTask = testGatling := {
    val classPath = fullClasspath.all(scopeProjectA).value.flatten

    def extractProjectAJar(toFolder: File) =
      classPath.find(_.data.toURI.toURL.toString.endsWith(s"project-A-$VersionVehiclesGatling.jar"))
        .map { jar => IO.unzip(new File(jar.data.toURI.toURL.getFile), toFolder)}

    val targetFolder = target.in(gatlingTests).value.getAbsolutePath
    val projectAExtractDir = new File(s"$targetFolder/projectAExtract")
    IO.delete(projectAExtractDir)
    projectAExtractDir.mkdirs()
    extractProjectAJar(v)
    System.setProperty("gatling.core.disableCompiler", "true")
    runProject(
      classPath,
      None,
      runJavaMain(
        mainClassName = "io.gatling.app.Gatling",
        args = Array(
          "--simulation", "full.path.to.a.gatling.Simulation",
          "--data-folder", s"${projectAExtractDir.getAbsolutePath}/data",
          "--results-folder", s"$targetFolder/gatling",
          "--request-bodies-folder", s"$targetFolder/request-bodies"
        ),
        method = "runGatling"
      )
    ) match {
      case 0 => println("Gatling execution SUCCESS")
      case exitCode =>
        println("Gatling execution FAILURE")
        throw new Exception(s"Gatling run exited with error $exitCode")
    }
  }

If anyone is trying to achieve the same thing I can give more details. The code is going to be open sourced soon anyway so it will be much easier to add references and examples.

Lucho

Hi There,

I am trying to do the same thing in my spring - gradle based project.
1- created the JAR (including all dependencies and simulation classes) and uploaded to artifactory. Then I added the jar as dependency in my application project and when I try to run this test using a build.gradle task in my application project, the gatling main class is unable to find the simulations. any input from you will be really helpful.

I have this question referred in detail in following page:
https://groups.google.com/forum/#!topic/gatling/DWy0HdNjhIQ