├── .gitignore ├── src ├── main │ ├── resources │ │ ├── har │ │ │ └── readme.txt │ │ └── config │ │ │ ├── recorder.conf │ │ │ └── gatling-recorder.properties │ └── scala │ │ └── nl │ │ └── jpoint │ │ └── gatling │ │ ├── GatlingProperties.scala │ │ ├── PathHelper.scala │ │ └── GatlingRecorderWrapper.scala └── test │ ├── scala │ └── simulations │ │ ├── readme.txt │ │ └── GoogleSimulation.scala │ ├── resources │ ├── request-bodies │ │ └── readme.txt │ ├── gatling-akka.conf │ ├── logback-test.xml │ └── gatling.conf │ └── readme.txt ├── README.md └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .idea/* 3 | target/ -------------------------------------------------------------------------------- /src/main/resources/har/readme.txt: -------------------------------------------------------------------------------- 1 | Put exported HAR files here. -------------------------------------------------------------------------------- /src/test/scala/simulations/readme.txt: -------------------------------------------------------------------------------- 1 | Put generated simulations here. -------------------------------------------------------------------------------- /src/test/resources/request-bodies/readme.txt: -------------------------------------------------------------------------------- 1 | Put recorded request bodies here. -------------------------------------------------------------------------------- /src/main/resources/config/recorder.conf: -------------------------------------------------------------------------------- 1 | // empty config file, necessary to start the recorder. -------------------------------------------------------------------------------- /src/test/readme.txt: -------------------------------------------------------------------------------- 1 | This folder contains generated performance tests: the Gatling Scala DSL and accompanying request bodies (for PUT and POSTS requests). -------------------------------------------------------------------------------- /src/main/resources/config/gatling-recorder.properties: -------------------------------------------------------------------------------- 1 | har=www.google.nl.har 2 | recorderWhiteList=(.*)www\\.google\\.nl(.+) 3 | simulationClassName=MyFirstGatlingSimulation 4 | -------------------------------------------------------------------------------- /src/main/scala/nl/jpoint/gatling/GatlingProperties.scala: -------------------------------------------------------------------------------- 1 | package nl.jpoint.gatling 2 | 3 | object GatlingProperties { 4 | val SIMULATION_CLASS_NAME: String = "simulationClassName" 5 | val RECORDER_WHITE_LIST: String = "recorderWhiteList" 6 | } 7 | -------------------------------------------------------------------------------- /src/test/resources/gatling-akka.conf: -------------------------------------------------------------------------------- 1 | akka { 2 | #loggers = ["akka.event.slf4j.Slf4jLogger"] 3 | #logging-filter = "akka.event.slf4j.Slf4jLoggingFilter" 4 | #log-dead-letters = off 5 | actor { 6 | default-dispatcher { 7 | #throughput = 20 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | %d{HH:mm:ss.SSS} [%-5level] %logger{15} - %msg%n%rEx 7 | 8 | false 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/test/scala/simulations/GoogleSimulation.scala: -------------------------------------------------------------------------------- 1 | package simulations 2 | 3 | import io.gatling.core.Predef._ 4 | import io.gatling.http.Predef._ 5 | import scala.concurrent.duration._ 6 | 7 | class GoogleSimulation extends Simulation { 8 | 9 | val httpProtocol = http.baseUrl("https://www.google.nl").userAgentHeader("test") 10 | 11 | val myFirstScenario = scenario("Google search") 12 | .exec( 13 | http("Open the Google start page.") 14 | .get("/") 15 | .check(status.is(200)) 16 | ) 17 | .pause(1) 18 | .exec( 19 | http("Perform Google search on 'gatling'.") 20 | .get("/search?q=gatling") 21 | .check(status.is(200)) 22 | // .check( bodyString.saveAs( "RESPONSE_DATA" ) )) 23 | // .exec( session => { println("Response: " + session("RESPONSE_DATA")); session } 24 | .check(regex("Voordat je verdergaat naar Google Zoeken")) 25 | ) 26 | 27 | setUp(myFirstScenario.inject(rampUsers(1) during (5 seconds))).protocols(httpProtocol) 28 | 29 | } 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | gatling-seed 2 | ========== 3 | This project is a skeleton for performance tests with Gatling and Maven. You can use it to quickly bootstrap your Gatling test setup and development environment. 4 | 5 | 6 | Getting started 7 | --- 8 | Install the following dependencies: 9 | - Java 8 (Java 7 will probably work, not tested though) 10 | - Maven 3 11 | 12 | 13 | Recording a test 14 | --- 15 | To record a test, perform the following steps: 16 | - Click through the test path in your browser 17 | - Export the request flow as HAR file: standard available in Chrome, available as plugin in Firefox through a FireBug extension: http://www.softwareishard.com/blog/netexport/ 18 | - Add details of the HAR filename and simulation name in ```src/main/resources/config/gatling-recorder.json``` 19 | - Generate the Gatling script: ```mvn clean scala:run``` 20 | - The Gatling simulation will be generated in ```src/test/scala/simulations```. Accompanying request bodies (in case of POST/PUT requests) will be in ```src/test/resources/request-bodies``` 21 | 22 | Executing a test 23 | --- 24 | To execute a test, perform: 25 | ```mvn clean gatling:test``` 26 | 27 | 28 | Or, when multiple simulations present: 29 | ```mvn gatling:test -Dgatling.simulationClass=simulations.``` 30 | -------------------------------------------------------------------------------- /src/main/scala/nl/jpoint/gatling/PathHelper.scala: -------------------------------------------------------------------------------- 1 | package nl.jpoint.gatling 2 | 3 | import io.gatling.commons.shared.unstable.util.PathHelper.RichPath 4 | 5 | import java.nio.file.{Path, Paths} 6 | 7 | 8 | object PathHelper { 9 | 10 | val recorderConfUrl: Path = Paths.get(ClassLoader.getSystemResource("config/recorder.conf").toURI) 11 | val projectRootDir = recorderConfUrl.ancestor(4) 12 | val mavenTargetDirectory = projectRootDir / "target" 13 | val recorderOutputPath = mavenTargetDirectory / "recorder-output" 14 | val simulationOutputFolder = (recorderOutputPath / "simulations").toString 15 | val simulationRequestBodiesFolder = (recorderOutputPath / "bodies").toString 16 | val mavenSourcesDirectory = projectRootDir / "src" / "main" / "scala" 17 | val mavenTestSourcesDirectory = projectRootDir / "src" / "test" / "scala" 18 | val mavenResourcesDirectory = projectRootDir / "src" / "main" / "resources" 19 | val mavenTestResourcesDirectory = projectRootDir / "src" / "test" / "resources" 20 | val harInputDirectory = (mavenResourcesDirectory / "har" ).toString 21 | val gatlingImportConfig = (mavenResourcesDirectory / "config" / "gatling-recorder.properties").toString 22 | val simulationTargetFolder = (mavenTestSourcesDirectory).toString 23 | val requestBodiesTargetFolder = (mavenTestResourcesDirectory / "request-bodies").toString 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/scala/nl/jpoint/gatling/GatlingRecorderWrapper.scala: -------------------------------------------------------------------------------- 1 | package nl.jpoint.gatling 2 | 3 | import io.gatling.recorder.GatlingRecorder 4 | import io.gatling.recorder.config.{RecorderMode, RecorderPropertiesBuilder} 5 | import org.apache.commons.io.FileUtils 6 | 7 | import java.io.{File, FileInputStream} 8 | import java.util 9 | import java.util.Properties 10 | 11 | object GatlingRecorderWrapper extends App { 12 | 13 | def startRecorder(props: RecorderPropertiesBuilder, simulationClassName : String): Unit = { 14 | 15 | println("Using project root dir " + PathHelper.projectRootDir) 16 | 17 | props.simulationsFolder(PathHelper.simulationOutputFolder) 18 | props.resourcesFolder(PathHelper.requestBodiesTargetFolder) 19 | props.simulationClassName(simulationClassName) 20 | props.simulationPackage("simulations") 21 | props.mode(RecorderMode.Har) 22 | props.headless(true) 23 | props.automaticReferer(true) 24 | props.checkResponseBodies(false) 25 | props.followRedirect(true) 26 | props.inferHtmlResources(true) 27 | props.removeCacheHeaders(true) 28 | props.encoding("utf-8") 29 | props.thresholdForPauseCreation("100") 30 | props.filterStrategy("BlacklistFirst") 31 | props.blacklist(util.Arrays.asList( 32 | ".*\\.js", 33 | ".*\\.css", 34 | ".*\\.gif", 35 | ".*\\.jpeg", 36 | ".*\\.jpg", 37 | ".*\\.ico", 38 | ".*\\.woff", 39 | ".*\\.(t|o)tf", 40 | ".*\\.png", 41 | ".*\\.svg" 42 | )) 43 | 44 | val recorderConfig = props.build 45 | println("Using recorder config: " + recorderConfig) 46 | 47 | GatlingRecorder.fromMap(recorderConfig, Some(PathHelper.recorderConfUrl)) 48 | } 49 | 50 | def startRecorderFromConfig(): Unit = { 51 | val properties: Properties = new Properties() 52 | properties.load(new FileInputStream(PathHelper.gatlingImportConfig)); 53 | 54 | def har = properties.getProperty("har") 55 | def recorderWhiteList = properties.getProperty(GatlingProperties.RECORDER_WHITE_LIST) 56 | def simulationClassName = properties.getProperty(GatlingProperties.SIMULATION_CLASS_NAME) 57 | 58 | println("Got config:") 59 | println(" har='" + har + "'") 60 | println(" recorderWhiteList='" + recorderWhiteList + "'") 61 | println(" simulationClassName='" + simulationClassName + "'") 62 | 63 | var props = new RecorderPropertiesBuilder 64 | props.harFilePath(PathHelper.harInputDirectory + "/" + har) 65 | props.whitelist(util.Arrays.asList(recorderWhiteList)) 66 | GatlingRecorderWrapper.startRecorder(props, simulationClassName) 67 | 68 | } 69 | 70 | def copyGeneratedSimulation(): Unit = { 71 | // Copy generated simulation(s). 72 | //FileUtils.deleteDirectory(new File(PathHelper.simulationTargetFolder + "/simulations")) 73 | FileUtils.copyDirectory(new File(PathHelper.simulationOutputFolder), new File(PathHelper.simulationTargetFolder)) 74 | 75 | // Copy generated request bodie(s) - when present. 76 | def bodiesFolder = new File(PathHelper.simulationRequestBodiesFolder) 77 | if (bodiesFolder.exists()) { 78 | FileUtils.copyDirectory(bodiesFolder, new File(PathHelper.requestBodiesTargetFolder)) 79 | } 80 | 81 | } 82 | 83 | // Process config. 84 | GatlingRecorderWrapper.startRecorderFromConfig() 85 | 86 | // Copy generated simulation(s). 87 | GatlingRecorderWrapper.copyGeneratedSimulation() 88 | 89 | } 90 | 91 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | nl.jpoint.gatling 7 | gatling-seed 8 | 1.0-SNAPSHOT 9 | 10 | Gatling Seed 11 | 12 | 13 | 1.8 14 | 1.8 15 | UTF-8 16 | 3.6.1 17 | 3.1.2 18 | 4.4.1 19 | 20 | 21 | 22 | 23 | io.gatling.highcharts 24 | gatling-charts-highcharts 25 | ${gatling.version} 26 | 27 | 28 | commons-io 29 | commons-io 30 | 2.11.0 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | net.alchim31.maven 39 | scala-maven-plugin 40 | ${scala-maven-plugin.version} 41 | 42 | 43 | 44 | recorder 45 | nl.jpoint.gatling.GatlingRecorderWrapper 46 | 47 | 48 | 49 | 50 | 51 | 52 | compile 53 | testCompile 54 | 55 | 56 | 57 | -Xss100M 58 | 59 | 60 | -target:jvm-1.8 61 | -deprecation 62 | -feature 63 | -unchecked 64 | -language:implicitConversions 65 | -language:postfixOps 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | io.gatling 75 | gatling-maven-plugin 76 | ${gatling-plugin.version} 77 | 78 | src/test/scala/simulations 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /src/test/resources/gatling.conf: -------------------------------------------------------------------------------- 1 | ######################### 2 | # Gatling Configuration # 3 | ######################### 4 | 5 | # This file contains all the settings configurable for Gatling with their default values 6 | 7 | gatling { 8 | core { 9 | #outputDirectoryBaseName = "" # The prefix for each simulation result folder (then suffixed by the report generation timestamp) 10 | #runDescription = "" # The description for this simulation run, displayed in each report 11 | #encoding = "utf-8" # Encoding to use throughout Gatling for file and string manipulation 12 | #simulationClass = "" # The FQCN of the simulation to run (when used in conjunction with noReports, the simulation for which assertions will be validated) 13 | #elFileBodiesCacheMaxCapacity = 200 # Cache size for request body EL templates, set to 0 to disable 14 | #rawFileBodiesCacheMaxCapacity = 200 # Cache size for request body Raw templates, set to 0 to disable 15 | #rawFileBodiesInMemoryMaxSize = 1000 # Below this limit, raw file bodies will be cached in memory 16 | #pebbleFileBodiesCacheMaxCapacity = 200 # Cache size for request body Peeble templates, set to 0 to disable 17 | #shutdownTimeout = 5000 # Milliseconds to wait for the actor system to shutdown 18 | extract { 19 | regex { 20 | #cacheMaxCapacity = 200 # Cache size for the compiled regexes, set to 0 to disable caching 21 | } 22 | xpath { 23 | #cacheMaxCapacity = 200 # Cache size for the compiled XPath queries, set to 0 to disable caching 24 | } 25 | jsonPath { 26 | #cacheMaxCapacity = 200 # Cache size for the compiled jsonPath queries, set to 0 to disable caching 27 | #preferJackson = false # When set to true, prefer Jackson over Boon for JSON-related operations 28 | } 29 | css { 30 | #cacheMaxCapacity = 200 # Cache size for the compiled CSS selectors queries, set to 0 to disable caching 31 | } 32 | } 33 | directory { 34 | #simulations = user-files/simulations # Directory where simulation classes are located (for bundle packaging only) 35 | #resources = user-files/resources # Directory where resources, such as feeder files and request bodies are located (for bundle packaging only) 36 | #reportsOnly = "" # If set, name of report folder to look for in order to generate its report 37 | #binaries = "" # If set, name of the folder where compiles classes are located: Defaults to GATLING_HOME/target. 38 | #results = results # Name of the folder where all reports folder are located 39 | } 40 | } 41 | charting { 42 | #noReports = false # When set to true, don't generate HTML reports 43 | #maxPlotPerSeries = 1000 # Number of points per graph in Gatling reports 44 | #useGroupDurationMetric = false # Switch group timings from cumulated response time to group duration. 45 | indicators { 46 | #lowerBound = 800 # Lower bound for the requests' response time to track in the reports and the console summary 47 | #higherBound = 1200 # Higher bound for the requests' response time to track in the reports and the console summary 48 | #percentile1 = 50 # Value for the 1st percentile to track in the reports, the console summary and Graphite 49 | #percentile2 = 75 # Value for the 2nd percentile to track in the reports, the console summary and Graphite 50 | #percentile3 = 95 # Value for the 3rd percentile to track in the reports, the console summary and Graphite 51 | #percentile4 = 99 # Value for the 4th percentile to track in the reports, the console summary and Graphite 52 | } 53 | } 54 | http { 55 | #fetchedCssCacheMaxCapacity = 200 # Cache size for CSS parsed content, set to 0 to disable 56 | #fetchedHtmlCacheMaxCapacity = 200 # Cache size for HTML parsed content, set to 0 to disable 57 | #perUserCacheMaxCapacity = 200 # Per virtual user cache size, set to 0 to disable 58 | #warmUpUrl = "https://gatling.io" # The URL to use to warm-up the HTTP stack (blank means disabled) 59 | #enableGA = true # Very light Google Analytics, please support 60 | ssl { 61 | keyStore { 62 | #type = "" # Type of SSLContext's KeyManagers store 63 | #file = "" # Location of SSLContext's KeyManagers store 64 | #password = "" # Password for SSLContext's KeyManagers store 65 | #algorithm = "" # Algorithm used SSLContext's KeyManagers store 66 | } 67 | trustStore { 68 | #type = "" # Type of SSLContext's TrustManagers store 69 | #file = "" # Location of SSLContext's TrustManagers store 70 | #password = "" # Password for SSLContext's TrustManagers store 71 | #algorithm = "" # Algorithm used by SSLContext's TrustManagers store 72 | } 73 | } 74 | ahc { 75 | #connectTimeout = 10000 # Timeout in millis for establishing a TCP socket 76 | #handshakeTimeout = 10000 # Timeout in millis for performing TLS handshake 77 | #pooledConnectionIdleTimeout = 60000 # Timeout in millis for a connection to stay idle in the pool 78 | #maxRetry = 2 # Number of times that a request should be tried again 79 | #requestTimeout = 60000 # Timeout in millis for performing an HTTP request 80 | #enableSni = true # When set to true, enable Server Name indication (SNI) 81 | #enableHostnameVerification = false # When set to true, enable hostname verification: SSLEngine.setHttpsEndpointIdentificationAlgorithm("HTTPS") 82 | #useInsecureTrustManager = true # Use an insecure TrustManager that trusts all server certificates 83 | #filterInsecureCipherSuites = true # Turn to false to not filter out insecure and weak cipher suites 84 | #sslEnabledProtocols = [TLSv1.2, TLSv1.1, TLSv1] # Array of enabled protocols for HTTPS, if empty use the JDK defaults 85 | #sslEnabledCipherSuites = [] # Array of enabled cipher suites for HTTPS, if empty use the AHC defaults 86 | #sslSessionCacheSize = 0 # SSLSession cache size, set to 0 to use JDK's default 87 | #sslSessionTimeout = 0 # SSLSession timeout in seconds, set to 0 to use JDK's default (24h) 88 | #disableSslSessionResumption = false # if true, SSLSessions won't be resumed 89 | #useOpenSsl = true # if OpenSSL should be used instead of JSSE 90 | #useNativeTransport = false # if native transport should be used instead of Java NIO (requires netty-transport-native-epoll, currently Linux only) 91 | #enableZeroCopy = true # if zero-copy upload should be used if possible 92 | #tcpNoDelay = true 93 | #soReuseAddress = false 94 | #allocator = "pooled" # switch to unpooled for unpooled ByteBufAllocator 95 | #maxThreadLocalCharBufferSize = 200000 # Netty's default is 16k 96 | } 97 | dns { 98 | #queryTimeout = 5000 # Timeout in millis of each DNS query in millis 99 | #maxQueriesPerResolve = 6 # Maximum allowed number of DNS queries for a given name resolution 100 | } 101 | } 102 | jms { 103 | #replyTimeoutScanPeriod = 1000 # scan period for timedout reply messages 104 | } 105 | data { 106 | #writers = [console, file] # The list of DataWriters to which Gatling write simulation data (currently supported : console, file, graphite, jdbc) 107 | console { 108 | #light = false # When set to true, displays a light version without detailed request stats 109 | #writePeriod = 5 # Write interval, in seconds 110 | } 111 | file { 112 | #bufferSize = 8192 # FileDataWriter's internal data buffer size, in bytes 113 | } 114 | leak { 115 | #noActivityTimeout = 30 # Period, in seconds, for which Gatling may have no activity before considering a leak may be happening 116 | } 117 | graphite { 118 | #light = false # only send the all* stats 119 | #host = "localhost" # The host where the Carbon server is located 120 | #port = 2003 # The port to which the Carbon server listens to (2003 is default for plaintext, 2004 is default for pickle) 121 | #protocol = "tcp" # The protocol used to send data to Carbon (currently supported : "tcp", "udp") 122 | #rootPathPrefix = "gatling" # The common prefix of all metrics sent to Graphite 123 | #bufferSize = 8192 # Internal data buffer size, in bytes 124 | #writePeriod = 1 # Write period, in seconds 125 | } 126 | } 127 | } 128 | --------------------------------------------------------------------------------