← All articles
Tech

Run JMeter Tests with Maven

Apache JMeter is widely used for load and performance testing, while Apache Maven provides a repeatable build lifecycle for Java-based projects. Combining the two makes it possible to execute JMeter test plans from the build, parameterize them for multiple environments, and integrate performance tests into automated delivery workflows.

This article demonstrates one approach for running an existing JMeter .jmx test plan through Maven.

Version context: This article reflects the JMeter/Maven plugin ecosystem available when it was originally written. Plugin coordinates, configuration options, and reporting capabilities may differ in current versions, but the underlying pattern—build-driven test execution with environment-specific configuration—remains applicable.

Project Structure

Assume you already have a working JMeter test plan. Create a Maven project and add the JMeter file under:

src/test/jmeter

The Maven pom.xml can then be configured to control how the test runs.

Environment-Specific Configuration

Performance tests often need different endpoints, credentials, or other properties for development, staging, and production-like environments. Maven profiles provide one way to keep these values separated while using the same test plan.

The following example defines a sample stage profile:



<profiles>
	<profile>
		<id>Stage</id>
		<build>
			<plugins>
				<plugin>
					<groupId>com.lazerycode.jmeter</groupId>
					<artifactId>jmeter-maven-plugin</artifactId>
					<version>2.0.3</version>
					<executions>
						<execution>
							<id>jmeter-tests</id>
							<phase>verify</phase>
							<goals>
								<goal>jmeter</goal>
							</goals>
						</execution>
					</executions>
					<configuration>
						<propertiesUser>
							<host>stage.sample.com</host>
							<!-- Use port 443 for https -->
							<port>80</port>
							<username>admin</username>
							<password>password</password>
						</propertiesUser>
						<jmeterExtensions>
							<artifact>kg.apc:jmeter-plugins:pom:1.4.0</artifact>
						</jmeterExtensions>
					</configuration>
				</plugin>
			</plugins>
		</build>
	</profile>
</profiles>
View original Gist ↗

You can then activate the profile when running the test:



mvn clean -PStage verify
View original Gist ↗

The same pattern can be repeated for additional environments.

Protecting Sensitive Properties

Environment-specific profiles sometimes include usernames, passwords, or other secrets. Avoid leaving those values in plaintext when the build configuration is stored in source control.

The example below uses a Maven plugin to decrypt protected values at runtime:

<plugin>
	<groupId>org.codehaus.gmaven</groupId>
	<artifactId>groovy-maven-plugin</artifactId>
	<executions>
		<execution>
			<id>decrypt-passwords</id>
			<phase>verify</phase>
			<goals>
				<goal>execute</goal>
			</goals>
			<configuration>
				<source>
					import org.jasypt.properties.EncryptableProperties;
					import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;


					username = "default";
					password = "default";
					if(properties["JMETER_ENCRYPTION_PASSWORD"] != null) {
						StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
						encryptor.setPassword(properties["JMETER_ENCRYPTION_PASSWORD"]);
						Properties props = new EncryptableProperties((Properties)properties, encryptor);
						props.propertyNames().each{
							if (it == 'encrypted.stage.username') {
								username = props.getProperty(it);
							} else if (it == 'encrypted.stage.password') {
								password = props.getProperty(it);
							}
						}
					}


					project.properties["stage.username"] = username;
					project.properties["stage.password"] = password;
				</source>
			</configuration>
		</execution>
	</executions>
	<dependencies>
		<dependency>
			<groupId>org.jasypt</groupId>
			<artifactId>jasypt</artifactId>
			<version>1.9.2</version>
		</dependency>
	</dependencies>
</plugin>
View original Gist ↗

If each Maven profile carries its own encrypted values, ensure the required decryption configuration is available for every profile that needs it.

Generating Reports

Executing a performance test is only part of the workflow. The build should also preserve results in a form that can be reviewed after execution.

The following example extends the sample stage profile with JMeter reporting configuration:



<profiles>
	<profile>
		<id>Stage</id>
		<build>
			<plugins>
				<plugin>
					<groupId>com.lazerycode.jmeter</groupId>
					<artifactId>jmeter-maven-plugin</artifactId>
					<version>2.0.3</version>
					<executions>
						<execution>
							<id>jmeter-tests</id>
							<phase>verify</phase>
							<goals>
								<goal>jmeter</goal>
							</goals>
						</execution>
					</executions>
					<configuration>
						<propertiesUser>
							<host>stage.sample.com</host>
							<!-- Use port 443 for https -->
							<port>80</port>
							<username>${stage.username}</username>
							<password>${stage.password}</password>
							<jmeter.save.saveservice.output_format>csv</jmeter.save.saveservice.output_format>
							<jmeter.save.saveservice.bytes>true</jmeter.save.saveservice.bytes>
							<jmeter.save.saveservice.print_field_names>true</jmeter.save.saveservice.print_field_names>
							<jmeter.save.saveservice.label>true</jmeter.save.saveservice.label>
							<jmeter.save.saveservice.latency>true</jmeter.save.saveservice.latency>
							<jmeter.save.saveservice.response_code>true</jmeter.save.saveservice.response_code>
							<jmeter.save.saveservice.response_message>true</jmeter.save.saveservice.response_message>
							<jmeter.save.saveservice.successful>true</jmeter.save.saveservice.successful>
							<jmeter.save.saveservice.thread_counts>true</jmeter.save.saveservice.thread_counts>
							<jmeter.save.saveservice.thread_name>true</jmeter.save.saveservice.thread_name>
							<jmeter.save.saveservice.time>true</jmeter.save.saveservice.time>
						</propertiesUser>
						<jmeterExtensions>
							<artifact>kg.apc:jmeter-plugins:pom:1.4.0</artifact>
						</jmeterExtensions>
						<jmeterExtensions>
							<artifact>kg.apc:jmeter-plugins-json:jar:2.3</artifact>
						</jmeterExtensions>
						<propertiesSystem>
							<https.socket.protocols>TLSv1.2</https.socket.protocols>
						</propertiesSystem>
						<testResultsTimestamp>false</testResultsTimestamp>
						<appendResultsTimestamp>false</appendResultsTimestamp>
					</configuration>
				</plugin>
			</plugins>
		</build>
	</profile>
</profiles>
View original Gist ↗

A reporting plugin can then process the generated results:



<plugin>
	<artifactId>maven-antrun-plugin</artifactId>
	<executions>
		<execution>
			<phase>verify</phase>
			<configuration>
				<tasks>
					<mkdir dir="${basedir}/target/jmeter/results/dashboard" />
					<copy file="${basedir}/src/test/resources/reportgenerator.properties"
						tofile="${basedir}/target/jmeter/bin/reportgenerator.properties" />
					<copy todir="${basedir}/target/jmeter/bin/report-template">
						<fileset dir="${basedir}/src/test/resources/report-template" />
					</copy>
					<java jar="${basedir}/target/jmeter/bin/ApacheJMeter-3.0.jar"
						fork="true">
						<arg value="-g" />
						<arg value="${basedir}/target/jmeter/results/<your_jmeter>.jtl" />
						<arg value="-o" />
						<arg value="${basedir}/target/jmeter/results/dashboard/" />
					</java>
				</tasks>
			</configuration>
			<goals>
				<goal>run</goal>
			</goals>
		</execution>
	</executions>
</plugin>
View original Gist ↗

Complete Maven Configuration

The following pom.xml brings the example together. Update the JMeter file name and environment-specific values for your project.



<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.sample</groupId>
	<artifactId>sample</artifactId>
	<version>1.0.0-SNAPSHOT</version>


	<properties>
		<encrypted.stage.username>ENC(encrypted_username)</encrypted.stage.username>
		<encrypted.stage.password>ENC(encrypted_password)</encrypted.stage.password>
	</properties>


	<profiles>
		<profile>
			<id>Stage</id>
			<build>
				<plugins>
					<plugin>
						<groupId>org.codehaus.gmaven</groupId>
						<artifactId>groovy-maven-plugin</artifactId>
						<executions>
							<execution>
								<id>decrypt-passwords</id>
								<phase>verify</phase>
								<goals>
									<goal>execute</goal>
								</goals>
								<configuration>
									<source>
										import org.jasypt.properties.EncryptableProperties;
										import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;


										username = "default";
										password = "default";
										if(properties["JMETER_ENCRYPTION_PASSWORD"] != null) {
											StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
											encryptor.setPassword(properties["JMETER_ENCRYPTION_PASSWORD"]);
											Properties props = new EncryptableProperties((Properties)properties, encryptor);
											props.propertyNames().each{
												if (it == 'encrypted.stage.username') {
													username = props.getProperty(it);
												} else if (it == 'encrypted.stage.password') {
													password = props.getProperty(it);
												}
											}
										}


										project.properties["stage.username"] = username;
										project.properties["stage.password"] = password;
									</source>
								</configuration>
							</execution>
						</executions>
						<dependencies>
							<dependency>
								<groupId>org.jasypt</groupId>
								<artifactId>jasypt</artifactId>
								<version>1.9.2</version>
							</dependency>
						</dependencies>
					</plugin>
					<plugin>
						<groupId>com.lazerycode.jmeter</groupId>
						<artifactId>jmeter-maven-plugin</artifactId>
						<version>2.0.3</version>
						<executions>
							<execution>
								<id>jmeter-tests</id>
								<phase>verify</phase>
								<goals>
									<goal>jmeter</goal>
								</goals>
							</execution>
						</executions>
						<configuration>
							<propertiesUser>
								<host>stage.sample.com</host>
								<!-- Use port 443 for https -->
								<port>80</port>
								<username>${stage.username}</username>
								<password>${stage.password}</password>
								<jmeter.save.saveservice.output_format>csv</jmeter.save.saveservice.output_format>
								<jmeter.save.saveservice.bytes>true</jmeter.save.saveservice.bytes>
								<jmeter.save.saveservice.print_field_names>true</jmeter.save.saveservice.print_field_names>
								<jmeter.save.saveservice.label>true</jmeter.save.saveservice.label>
								<jmeter.save.saveservice.latency>true</jmeter.save.saveservice.latency>
								<jmeter.save.saveservice.response_code>true</jmeter.save.saveservice.response_code>
								<jmeter.save.saveservice.response_message>true</jmeter.save.saveservice.response_message>
								<jmeter.save.saveservice.successful>true</jmeter.save.saveservice.successful>
								<jmeter.save.saveservice.thread_counts>true</jmeter.save.saveservice.thread_counts>
								<jmeter.save.saveservice.thread_name>true</jmeter.save.saveservice.thread_name>
								<jmeter.save.saveservice.time>true</jmeter.save.saveservice.time>
							</propertiesUser>
							<jmeterExtensions>
								<artifact>kg.apc:jmeter-plugins:pom:1.4.0</artifact>
							</jmeterExtensions>
							<jmeterExtensions>
								<artifact>kg.apc:jmeter-plugins-json:jar:2.3</artifact>
							</jmeterExtensions>
							<propertiesSystem>
								<https.socket.protocols>TLSv1.2</https.socket.protocols>
							</propertiesSystem>
							<testResultsTimestamp>false</testResultsTimestamp>
							<appendResultsTimestamp>false</appendResultsTimestamp>
						</configuration>
					</plugin>
					<plugin>
						<artifactId>maven-antrun-plugin</artifactId>
						<executions>
							<execution>
								<phase>verify</phase>
								<configuration>
									<tasks>
										<mkdir dir="${basedir}/target/jmeter/results/dashboard" />
										<copy file="${basedir}/src/test/resources/reportgenerator.properties"
											tofile="${basedir}/target/jmeter/bin/reportgenerator.properties" />
										<copy todir="${basedir}/target/jmeter/bin/report-template">
											<fileset dir="${basedir}/src/test/resources/report-template" />
										</copy>
										<java jar="${basedir}/target/jmeter/bin/ApacheJMeter-3.0.jar"
											fork="true">
											<arg value="-g" />
											<arg value="${basedir}/target/jmeter/results/<your_jmeter>.jtl" />
											<arg value="-o" />
											<arg value="${basedir}/target/jmeter/results/dashboard/" />
										</java>
									</tasks>
								</configuration>
								<goals>
									<goal>run</goal>
								</goals>
							</execution>
						</executions>
					</plugin>
				</plugins>
			</build>
		</profile>
	</profiles>


</project>
View original Gist ↗

Run the test by activating the appropriate Maven profile and supplying the required decryption password:



mvn clean -PStage verify -DJMETER_ENCRYPTION_PASSWORD=password
View original Gist ↗

Why This Pattern Is Useful

Moving JMeter execution into Maven provides a repeatable command-line workflow instead of depending on manual execution from the JMeter UI. It also makes the tests easier to run from CI systems, standardizes environment configuration, and gives teams a common place to manage test execution and reporting.

Takeaway

The main benefit is not Maven itself; it is repeatability. A performance test becomes significantly more useful when the same test plan can be executed consistently across environments, supplied with controlled configuration, and incorporated into an automated build or delivery process.