Skip to main content

JMeter

JMeter is an integral part of Testkube. The Testkube JMeter executor is installed by default during the Testkube installation.

Default command for this executor: <entryPoint> Default arguments for this executor command: -n -j <logFile> -t <runPath> -l <jtlFile> -e -o <reportFile> <envVars> (parameters in <> are calculated at test execution)

🎓What is JMeter?
  • Apache JMeter is an open-source, pure Java application designed to load test functional behavior and measure performance. It was originally designed for testing Web Applications but has since expanded to other test functions.
What can I do with JMeter?
  • JMeter can be used to test performance both on static and dynamic resources and Web dynamic applications.
  • It can also be used to simulate a heavy load on a server, group of servers, network, or object to test its strength or to analyze overall performance under different load types.

Check out our blog post to follow tutorial steps for end-to-end testing of your Kubernetes applications with JMeter.

Running a JMeter Test

Using Files as Input

Let's save our JMeter test in file e.g. test.jmx.

<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="5.0" jmeter="5.5">
<hashTree>
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Kubeshop site" enabled="true">
<stringProp name="TestPlan.comments">Kubeshop site simple perf test</stringProp>
<boolProp name="TestPlan.functional_mode">false</boolProp>
<boolProp name="TestPlan.tearDown_on_shutdown">true</boolProp>
<boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
<elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments">
<elementProp name="PATH" elementType="Argument">
<stringProp name="Argument.name">PATH</stringProp>
<stringProp name="Argument.value">/pricing</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
</collectionProp>
</elementProp>
<stringProp name="TestPlan.user_define_classpath"></stringProp>
</TestPlan>
<hashTree>
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Thread Group" enabled="true">
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
<boolProp name="LoopController.continue_forever">false</boolProp>
<stringProp name="LoopController.loops">1</stringProp>
</elementProp>
<stringProp name="ThreadGroup.num_threads">1</stringProp>
<stringProp name="ThreadGroup.ramp_time">1</stringProp>
<boolProp name="ThreadGroup.scheduler">false</boolProp>
<stringProp name="ThreadGroup.duration"></stringProp>
<stringProp name="ThreadGroup.delay"></stringProp>
<boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp>
</ThreadGroup>
<hashTree>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="HTTP Request" enabled="true">
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments">
<elementProp name="PATH" elementType="HTTPArgument">
<boolProp name="HTTPArgument.always_encode">false</boolProp>
<stringProp name="Argument.value">$PATH</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
<boolProp name="HTTPArgument.use_equals">true</boolProp>
<stringProp name="Argument.name">PATH</stringProp>
</elementProp>
</collectionProp>
</elementProp>
<stringProp name="HTTPSampler.domain">testkube.io</stringProp>
<stringProp name="HTTPSampler.port">80</stringProp>
<stringProp name="HTTPSampler.protocol">https</stringProp>
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
<stringProp name="HTTPSampler.path">https://testkube.io</stringProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
</HTTPSamplerProxy>
<hashTree>
<ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true">
<collectionProp name="Asserion.test_strings">
<stringProp name="-1081444641">Testkube</stringProp>
</collectionProp>
<stringProp name="Assertion.custom_message"></stringProp>
<stringProp name="Assertion.test_field">Assertion.response_data</stringProp>
<boolProp name="Assertion.assume_success">false</boolProp>
<intProp name="Assertion.test_type">16</intProp>
</ResponseAssertion>
<hashTree/>
</hashTree>
</hashTree>
</hashTree>
</hashTree>
</jmeterTestPlan>

The Testkube JMeter executor accepts a test file as an input.

kubectl testkube create test --file test.jmx --name jmeter-test --type jmeter/test

You don't need to pass a type here, Testkube will autodetect it.

To run the test, pass the previously created test name:

kubectl testkube run test -f jmeter-test

You can also create a Test based on a Git repository:

# create test from this Git repository
kubectl testkube create test --test-content-type git --git-uri https://github.com/kubeshop/testkube-executor-jmeter.git --git-branch main --git-path examples/kubeshop.jmx --type jmeter/test --name jmeter-test-from-git

Testkube will clone the repository and create a Testkube Test Custom Resource in your cluster automatically on each test run.

Using Additional JMeter Arguments in Your Tests

You can also pass additional arguments to the jmeter binary thanks to the --args flag:

$ kubectl testkube run test -f jmeter-test --args '-LsutHost=https://staging.kubeshop.com -LsomeParam=someValue'

JMeter Test Results

A JMeter test will be successful in Testkube when all checks and thresholds are successful. In the case of an error, the test will have failed status, and the JMeter executor is configured to store the report.jtl file after the test run. You can get the file from the "Artifacts" tab in the execution results in the Testkube Dashboard, or download it with the testkube get artifacts EXECUTION_ID command.

JMeter Memory Consumption

When running load tests, it is common to run into memory-related issues, for example the OOMKilled status in Kubernetes. There are three areas where the memory requests and limits can be set:

  • on JVM level, inside the pod
  • on pod/job level
  • on cluster level

In most cases, it is wise to start from setting the correct variables for the JVM. As the README of the underlying image suggests, the available values are JVM_XMN, JVM_XMS and JVM_XMX.

By default, JMeter reads out the available memory from the host machine and uses a fixed value of 80% of it as a maximum. If this causes Issues, there is the option to use environment variables to adjust the JVM memory Parameters:

JVM_XMN to adjust maximum nursery size

JVM_XMS to adjust initial heap size

JVM_XMX to adjust maximum heap size

All three use values in Megabyte range.

If this does not fix your issue, look into using Testkube job templates to set job-level requests and limits.

In the very rare case that you are still bumping into memory issues, consider asking your Kubernetes cluster manager about any resource quotas.