├── .gitignore ├── docker └── jenkins │ ├── init.groovy │ ├── plugins.txt │ ├── hudson.tasks.Maven.xml │ ├── settings.xml │ ├── plugins.sh │ ├── jenkins.sh │ ├── Dockerfile │ ├── config.xml │ └── jobs │ └── jmeter-maven-example │ └── config.xml ├── src ├── main │ ├── webapp │ │ └── WEB-INF │ │ │ └── web.xml │ └── java │ │ └── de │ │ └── codecentric │ │ └── jmeter │ │ └── example │ │ └── ExampleServlet.java └── test │ └── jmeter │ └── SimpleWebservicePerformanceTest.jmx ├── README.md └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.iml 3 | target 4 | *.log -------------------------------------------------------------------------------- /docker/jenkins/init.groovy: -------------------------------------------------------------------------------- 1 | import hudson.model.*; 2 | import jenkins.model.*; 3 | 4 | 5 | Thread.start { 6 | sleep 10000 7 | println "--> setting agent port for jnlp" 8 | Jenkins.instance.setSlaveAgentPort(50000) 9 | } 10 | -------------------------------------------------------------------------------- /docker/jenkins/plugins.txt: -------------------------------------------------------------------------------- 1 | copyartifact:1.35 2 | git-client:1.16.1 3 | github-api:1.59 4 | github:1.11 5 | git:2.3.4 6 | jquery:1.7.2-1 7 | mercurial:1.51 8 | ruby-runtime:0.12 9 | scm-api:0.2 10 | token-macro:1.10 11 | parameterized-trigger:2.25 12 | -------------------------------------------------------------------------------- /docker/jenkins/hudson.tasks.Maven.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Maven 6 | /usr/share/maven 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | example 4 | de.codecentric.jmeter.example.ExampleServlet 5 | 6 | 7 | example 8 | /* 9 | 10 | -------------------------------------------------------------------------------- /docker/jenkins/settings.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /docker/jenkins/plugins.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # Parse a support-core plugin -style txt file as specification for jenkins plugins to be installed 4 | # in the reference directory, so user can define a derived Docker image with just : 5 | # 6 | # FROM jenkins 7 | # COPY plugins.txt /plugins.txt 8 | # RUN /usr/share/jenkins/plugins.sh /plugins.txt 9 | # 10 | 11 | REF=/usr/share/jenkins/ref/plugins 12 | mkdir -p $REF 13 | 14 | while read spec; do 15 | plugin=(${spec//:/ }); 16 | curl -L ${JENKINS_UC}/download/plugins/${plugin[0]}/${plugin[1]}/${plugin[0]}.hpi -o $REF/${plugin[0]}.hpi; 17 | done < $1 18 | -------------------------------------------------------------------------------- /src/main/java/de/codecentric/jmeter/example/ExampleServlet.java: -------------------------------------------------------------------------------- 1 | package de.codecentric.jmeter.example; 2 | 3 | import javax.servlet.GenericServlet; 4 | import javax.servlet.ServletException; 5 | import javax.servlet.ServletRequest; 6 | import javax.servlet.ServletResponse; 7 | import java.io.IOException; 8 | import java.util.Random; 9 | 10 | 11 | public class ExampleServlet extends GenericServlet { 12 | private Random random = new Random(); 13 | 14 | @Override 15 | public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { 16 | sleep(100); 17 | res.getOutputStream().print("Hello ..."); 18 | res.getOutputStream().flush(); 19 | sleep(100); 20 | res.getOutputStream().print("... World"); 21 | } 22 | 23 | private void sleep(int interval) { 24 | try { 25 | // Wait a random time between 0.5*interval and 1.5*interval msec 26 | Thread.sleep(random.nextInt(interval) + interval/2); 27 | } catch (InterruptedException e) { 28 | // Don't care if we've been interrupted 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /docker/jenkins/jenkins.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # Copy files from /usr/share/jenkins/ref into /var/jenkins_home 4 | # So the initial JENKINS-HOME is set with expected content. 5 | # Don't override, as this is just a reference setup, and use from UI 6 | # can then change this, upgrade plugins, etc. 7 | copy_reference_file() { 8 | f=${1%/} 9 | echo "$f" 10 | rel=${f:23} 11 | dir=$(dirname ${f}) 12 | echo " $f -> $rel" 13 | if [[ ! -e /var/jenkins_home/${rel} ]] 14 | then 15 | echo "copy $rel to JENKINS_HOME" 16 | mkdir -p /var/jenkins_home/${dir:23} 17 | cp -r /usr/share/jenkins/ref/${rel} /var/jenkins_home/${rel}; 18 | fi; 19 | } 20 | export -f copy_reference_file 21 | find /usr/share/jenkins/ref/ -type f -exec bash -c 'copy_reference_file {}' \; 22 | 23 | # if `docker run` first argument start with `--` the user is passing jenkins launcher arguments 24 | if [[ $# -lt 1 ]] || [[ "$1" == "--"* ]]; then 25 | exec java $JAVA_OPTS -jar /usr/share/jenkins/jenkins.war $JENKINS_OPTS "$@" 26 | fi 27 | 28 | # As argument is not jenkins, assume user want to run his own process, for sample a `bash` shell to explore this image 29 | exec "$@" 30 | 31 | -------------------------------------------------------------------------------- /docker/jenkins/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM java:8-jdk 2 | 3 | RUN apt-get update && apt-get install -y wget git curl maven zip && rm -rf /var/lib/apt/lists/* 4 | 5 | ENV JENKINS_HOME /var/jenkins_home 6 | 7 | # Jenkins is ran with user `jenkins`, uid = 1000 8 | # If you bind mount a volume from host/vloume from a data container, 9 | # ensure you use same uid 10 | RUN useradd -d "$JENKINS_HOME" -u 1000 -m -s /bin/bash jenkins 11 | 12 | # Jenkins home directoy is a volume, so configuration and build history 13 | # can be persisted and survive image upgrades 14 | VOLUME /var/jenkins_home 15 | 16 | # `/usr/share/jenkins/ref/` contains all reference configuration we want 17 | # to set on a fresh new installation. Use it to bundle additional plugins 18 | # or config file with your custom jenkins Docker image. 19 | RUN mkdir -p /usr/share/jenkins/ref/init.groovy.d 20 | 21 | COPY init.groovy /usr/share/jenkins/ref/init.groovy.d/tcp-slave-angent-port.groovy 22 | 23 | ENV JENKINS_VERSION 1.601 24 | 25 | # could use ADD but this one does not check Last-Modified header 26 | # see https://github.com/docker/docker/issues/8331 27 | RUN curl -L http://mirrors.jenkins-ci.org/war/1.601/jenkins.war -o /usr/share/jenkins/jenkins.war 28 | 29 | ENV JENKINS_UC https://updates.jenkins-ci.org 30 | RUN chown -R jenkins "$JENKINS_HOME" /usr/share/jenkins/ref 31 | 32 | # for main web interface: 33 | EXPOSE 8080 34 | 35 | # will be used by attached slave agents: 36 | EXPOSE 50000 37 | 38 | COPY jenkins.sh /usr/local/bin/jenkins.sh 39 | 40 | ENTRYPOINT ["/usr/local/bin/jenkins.sh"] 41 | 42 | # from a derived Dockerfile, can use `RUN plugin.sh active.txt` to setup /usr/share/jenkins/ref/plugins from a support bundle 43 | COPY plugins.sh /usr/local/bin/plugins.sh 44 | COPY plugins.txt /usr/share/jenkins/ref/plugins.txt 45 | RUN plugins.sh /usr/share/jenkins/ref/plugins.txt 46 | 47 | # Here comes our special scrum-for-developers-training configuration 48 | COPY settings.xml /usr/share/jenkins/ref/.m2/settings.xml 49 | COPY config.xml /usr/share/jenkins/ref/config.xml 50 | COPY org.jfrog.hudson.ArtifactoryBuilder.xml /usr/share/jenkins/ref/org.jfrog.hudson.ArtifactoryBuilder.xml 51 | COPY hudson.tasks.Maven.xml /usr/share/jenkins/ref/hudson.tasks.Maven.xml 52 | ADD jobs /usr/share/jenkins/ref/jobs 53 | 54 | ENV JENKINS_OPTS --prefix=/jenkins 55 | 56 | USER jenkins 57 | -------------------------------------------------------------------------------- /docker/jenkins/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1.601 5 | 2 6 | NORMAL 7 | true 8 | 9 | 10 | false 11 | 12 | ${ITEM_ROOTDIR}/workspace 13 | ${ITEM_ROOTDIR}/builds 14 | 15 | false 16 | 17 | 18 | 19 | JDK 20 | /usr/lib/jvm/java-8-openjdk-amd64 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 5 29 | 0 30 | 31 | 32 | 33 | All 34 | false 35 | false 36 | 37 | 38 | 39 | 40 | Pipeline 41 | false 42 | false 43 | 44 | 45 | 46 | Worblehat 47 | worblehat-010-build 48 | 49 | 50 | 5 51 | false 52 | 1 53 | none 54 | false 55 | 2 56 | true 57 | true 58 | 59 | 60 | 61 | All 62 | 50000 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JMeter Maven Example 2 | Example how to integrate jmeter tests in a maven build and how to automatically generate graphs from the test results using the jmeter plugins CMDRunner. 3 | 4 | The jmeter tests can easily be used as part of a jenkins-job. See https://mlex.ci.cloudbees.com/job/jmeter-maven-example/. The repository also contains a docker-image of a preconfigured jenkins. 5 | 6 | This example was created to accompany a blog post: https://blog.codecentric.de/2013/12/jmeter-tests-mit-maven-und-jenkins-automatisieren/ 7 | 8 | The [jmeter-maven-plugin](https://github.com/Ronnie76er/jmeter-maven-plugin) is used to integrate jmeter in the maven build. To generate graphs from the jmeter results, the [jmeter-graph-maven-plugin](https://github.com/codecentric/jmeter-graph-maven-plugin) is used. 9 | 10 | ## Jenkins 11 | Under `docker/jenkins` can find a jenkins with a preconfigured build-job that runs the jmeter tests and archives the results. Just build the docker image with 12 | ``` 13 | cd docker/jenkins 14 | docker build -t jmeter-jenkins . 15 | ``` 16 | 17 | and run the container with 18 | ``` 19 | docker run -p=8080:8080 jmeter-jenkins 20 | ``` 21 | and you can access the jenkins in your browser via `http://localhost:8080/jenkins/`. 22 | 23 | ## Quickstart 24 | Just execute 25 | 26 | ```bash 27 | mvn -Pembedded-jetty verify 28 | ``` 29 | 30 | This will 31 | * start an embedded jetty server wit a small webapp, 32 | * run jmeter tests (just some http requests) against this webserver and 33 | * create some nice graphs of the result (you will find them in `target/jmeter/results`). 34 | 35 | ## JMeter GUI 36 | 37 | To start the JMeter GUI, use the `jmeter:gui` goal. The tests are located in `/src/test/jmeter`. If you start the tests, make sure that the example webapp is running. You can start the webapp explicitly with `jetty:run`. 38 | 39 | ## JMeter Headless 40 | 41 | To just execute the jmeter-tests from commandline (without gui, without embedded webapp, without graph-generation), use the `jmeter:jmeter` goal. The tests expect a running example webapp, so make sure at `http://localhost:9097/`. The results of the test-run can be found in `/target/jmeter/results`. If you want graph-generation, run `mvn verify` (without the "local" profile). 42 | 43 | ## Configuration 44 | The following maven-properties are available (to set them from commandline, simply add `-Dproperty=value`) 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 |
PropertyDefault
jetty.port9097
performancetest.webservice.hostlocalhost
performancetest.webservice.port${jetty.port}
performancetest.webservice.path/
performancetest.connectTimeout1000
performancetest.responseTimeout3000
performancetest.threadCount20
performancetest.loopCount10
84 | -------------------------------------------------------------------------------- /docker/jenkins/jobs/jmeter-maven-example/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <h2>Result of the last successfull build</h2> 5 | <p><a href="lastSuccessfulBuild/artifact/target/jmeter/results/SimpleWebservicePerformanceTest.jtl">Test results (as JTL-File)</a></p> 6 | <table width="100%"> 7 | <tr> 8 | <td width="33%"> 9 | <img width="100%" src="lastSuccessfulBuild/artifact/target/jmeter/results/SimpleWebservicePerformanceTest-ResponseTimesOverTime.png"/> 10 | </td> 11 | <td width="33%"> 12 | <img width="100%" src="lastSuccessfulBuild/artifact/target/jmeter/results/SimpleWebservicePerformanceTest-ThreadsStateOverTime.png"/> 13 | </td> 14 | <td width="33%"> 15 | <img width="100%" src="lastSuccessfulBuild/artifact/target/jmeter/results/SimpleWebservicePerformanceTest-TransactionsPerSecond.png"/> 16 | </td> 17 | </tr> 18 | </table> 19 | 20 | 60 21 | -1 22 | -1 23 | -1 24 | 25 | false 26 | 27 | 28 | 29 | 30 | THREAD_COUNT 31 | Number of parallel threads for the load test. 32 | 10 33 | 34 | 35 | LOOP_COUNT 36 | Number of iterations for each user thread. 37 | 10 38 | 39 | 40 | 41 | 42 | 43 | 2 44 | 45 | 46 | https://github.com/mlex/jmeter-maven-example 47 | 48 | 49 | 50 | 51 | */master 52 | 53 | 54 | false 55 | 56 | 57 | 58 | true 59 | false 60 | false 61 | false 62 | 63 | false 64 | 65 | de.codecentric.jmeter 66 | jmeter-maven-example 67 | 68 | clean -Pembedded-jetty verify -Dperformancetest.threadCount=$THREAD_COUNT -Dperformancetest.loopCount=$LOOP_COUNT 69 | true 70 | false 71 | false 72 | false 73 | false 74 | false 75 | false 76 | false 77 | -1 78 | false 79 | false 80 | true 81 | 82 | 83 | 84 | 85 | 86 | target/jmeter/results/* 87 | false 88 | false 89 | false 90 | true 91 | 92 | 93 | 94 | 95 | 96 | 97 | FAILURE 98 | 2 99 | RED 100 | true 101 | 102 | 103 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | de.codecentric.jmeter 7 | jmeter-maven-example 8 | 0.1.0-SNAPSHOT 9 | jar 10 | JMeter Maven Example 11 | 12 | Example how to integrate jmeter tests in a maven build and how to automatically 13 | generate graphs from the test results using the jmeter plugins CMDRunner. 14 | 15 | 16 | 17 | 9097 18 | 19097 19 | localhost 20 | ${jetty.port} 21 | / 22 | 1000 23 | 3000 24 | 20 25 | 10 26 | 27 | 28 | 29 | 30 | javax.servlet 31 | servlet-api 32 | 2.5 33 | 34 | 35 | 36 | 37 | 38 | jenkins 39 | 40 | tomcat7-example-webapp.mlex.cloudbees.net 41 | 80 42 | 5 43 | 5 44 | 45 | 46 | 47 | embedded-jetty 48 | 49 | 50 | 51 | org.mortbay.jetty 52 | jetty-maven-plugin 53 | 54 | 55 | start-jetty 56 | 57 | start 58 | 59 | pre-integration-test 60 | 61 | true 62 | 63 | 64 | 65 | stop-jetty 66 | 67 | stop 68 | 69 | post-integration-test 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | com.lazerycode.jmeter 82 | jmeter-maven-plugin 83 | 1.9.0 84 | 85 | false 86 | DEBUG 87 | false 88 | true 89 | 90 | ${performancetest.webservice.host} 91 | ${performancetest.webservice.port} 92 | ${performancetest.webservice.path} 93 | ${performancetest.connectTimeout} 94 | ${performancetest.responseTimeout} 95 | ${performancetest.threadCount} 96 | ${performancetest.threadCount} 97 | 98 | 99 | true 100 | 101 | 102 | 103 | kg.apc 104 | jmeter-plugins 105 | 106 | 107 | 108 | 109 | 110 | execute-jmeter-tests 111 | 112 | jmeter 113 | 114 | integration-test 115 | 116 | 117 | 118 | 119 | kg.apc 120 | jmeter-plugins 121 | 1.0.0 122 | 123 | 128 | 129 | kg.apc 130 | perfmon 131 | 132 | 133 | org.apache.hadoop 134 | hadoop-core 135 | 136 | 137 | org.apache.hbase 138 | hbase 139 | 140 | 141 | 145 | 146 | org.apache.jmeter 147 | jorphan 148 | 149 | 150 | org.apache.bsf 151 | bsf-api 152 | 153 | 154 | org.bouncycastle 155 | bcmail-jdk15 156 | 157 | 158 | org.bouncycastle 159 | bcprov-jdk15 160 | 161 | 162 | javax.activation 163 | activation 164 | 165 | 166 | commons-logging 167 | commons-logging 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | com.lazerycode.jmeter 176 | jmeter-analysis-maven-plugin 177 | 1.0.4 178 | 179 | ${project.build.directory}/jmeter/results/SimpleWebservicePerformanceTest.jtl 180 | 181 | 182 | 183 | create-html-report 184 | verify 185 | 186 | analyze 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | org.mortbay.jetty 195 | jetty-maven-plugin 196 | 8.1.14.v20131031 197 | 198 | 199 | 200 | ${jetty.port} 201 | 202 | 203 | ${jetty.stopPort} 204 | STOP 205 | 206 | 207 | 208 | 209 | de.codecentric 210 | jmeter-graph-maven-plugin 211 | 0.1.0 212 | 213 | 214 | create-graph-threads 215 | 216 | create-graph 217 | 218 | verify 219 | 220 | 221 | 222 | ${project.build.directory}/jmeter/results/SimpleWebservicePerformanceTest.jtl 223 | 224 | 225 | ThreadsStateOverTime 226 | 800 227 | 600 228 | ${project.build.directory}/jmeter/results/SimpleWebservicePerformanceTest-ThreadsStateOverTime.png 229 | 230 | 231 | ResponseTimesOverTime 232 | 800 233 | 600 234 | ${project.build.directory}/jmeter/results/SimpleWebservicePerformanceTest-ResponseTimesOverTime.png 235 | 236 | 237 | TransactionsPerSecond 238 | 800 239 | 600 240 | ${project.build.directory}/jmeter/results/SimpleWebservicePerformanceTest-TransactionsPerSecond.png 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | -------------------------------------------------------------------------------- /src/test/jmeter/SimpleWebservicePerformanceTest.jmx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | false 7 | false 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | HOST 18 | ${__P(webservice.host,localhost)} 19 | Hostname (or IP) of the webservice to test. 20 | = 21 | 22 | 23 | PORT 24 | ${__P(webservice.port,8080)} 25 | Port of the webservice. 26 | = 27 | 28 | 29 | PATH 30 | ${__P(webservice.path,/)} 31 | Path to the webservice. 32 | = 33 | 34 | 35 | CONNECT_TIMEOUT 36 | ${__P(webservice.connectTimeout,1000)} 37 | Timeout when connecting to webservice. 38 | = 39 | 40 | 41 | RESPONSE_TIMEOUT 42 | ${__P(webservice.responseTimeout,3000)} 43 | Webservice response timeout. 44 | = 45 | 46 | 47 | THREADCOUNT 48 | ${__P(threadCount, 20)} 49 | Maximal number of concurrent threads. 50 | = 51 | 52 | 53 | LOOPCOUNT 54 | ${__P(loopCount, 10)} 55 | Number of iterations for each thread. 56 | = 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | ${HOST} 66 | ${PORT} 67 | ${CONNECT_TIMEOUT} 68 | ${RESPONSE_TIMEOUT} 69 | 70 | 71 | ${PATH} 72 | 4 73 | 74 | 75 | 76 | continue 77 | 78 | false 79 | ${LOOPCOUNT} 80 | 81 | ${THREADCOUNT} 82 | ${THREADCOUNT} 83 | 1386699377000 84 | 1386699377000 85 | false 86 | 87 | 88 | 89 | 90 | 91 | 200 92 | 200 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | GET 107 | true 108 | false 109 | true 110 | false 111 | false 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | false 120 | 121 | saveConfig 122 | 123 | 124 | true 125 | true 126 | true 127 | 128 | true 129 | true 130 | true 131 | true 132 | false 133 | true 134 | true 135 | false 136 | false 137 | false 138 | false 139 | false 140 | false 141 | false 142 | false 143 | 0 144 | true 145 | true 146 | 147 | 148 | 149 | 500 150 | false 151 | 152 | 153 | 154 | 155 | 156 | false 157 | 158 | saveConfig 159 | 160 | 161 | true 162 | true 163 | true 164 | 165 | true 166 | true 167 | true 168 | true 169 | false 170 | true 171 | true 172 | false 173 | false 174 | false 175 | false 176 | false 177 | false 178 | false 179 | false 180 | 0 181 | true 182 | true 183 | 184 | 185 | 186 | 500 187 | false 188 | 189 | 190 | 191 | 192 | 193 | false 194 | 195 | saveConfig 196 | 197 | 198 | true 199 | true 200 | true 201 | 202 | true 203 | true 204 | true 205 | true 206 | false 207 | true 208 | true 209 | false 210 | false 211 | false 212 | false 213 | false 214 | false 215 | false 216 | false 217 | 0 218 | true 219 | true 220 | 221 | 222 | 223 | 1000 224 | false 225 | 226 | 227 | 228 | 229 | 230 | false 231 | 232 | saveConfig 233 | 234 | 235 | true 236 | true 237 | true 238 | 239 | true 240 | true 241 | true 242 | true 243 | false 244 | true 245 | true 246 | false 247 | false 248 | false 249 | false 250 | false 251 | false 252 | false 253 | false 254 | 0 255 | true 256 | true 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | --------------------------------------------------------------------------------