seed-skills/jmeter-load/SKILL.md
Load and performance testing skill using Apache JMeter, covering test plans, thread groups, assertions, listeners, timers, and distributed testing.
npx skillsauth add PramodDutta/qaskills JMeter Load TestingInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
You are an expert performance engineer specializing in Apache JMeter. When the user asks you to create, review, or debug JMeter test plans, follow these detailed instructions.
jmeter/
test-plans/
smoke-test.jmx
load-test.jmx
stress-test.jmx
api-test.jmx
data/
users.csv
products.csv
payloads/
create-order.json
lib/
custom-plugins.jar
scripts/
run-load-test.sh
generate-report.sh
results/
.gitkeep
reports/
.gitkeep
jmeter.properties
A well-organized JMeter test plan follows this hierarchy:
Test Plan
├── User Defined Variables
├── HTTP Request Defaults
├── HTTP Header Manager
├── HTTP Cookie Manager
├── CSV Data Set Config
├── Thread Group (User Flow)
│ ├── Transaction Controller (Login)
│ │ ├── HTTP Request (GET /login)
│ │ ├── HTTP Request (POST /auth/login)
│ │ ├── Response Assertion
│ │ ├── JSON Extractor (token)
│ │ └── JSR223 PostProcessor
│ ├── Constant Timer (Think Time)
│ ├── Transaction Controller (Browse Products)
│ │ ├── HTTP Request (GET /products)
│ │ └── Response Assertion
│ └── Transaction Controller (Checkout)
│ ├── HTTP Request (POST /cart)
│ ├── HTTP Request (POST /checkout)
│ └── Response Assertion
├── View Results Tree (debug only)
├── Summary Report
└── Backend Listener (InfluxDB)
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Load Test Users">
<intProp name="ThreadGroup.num_threads">100</intProp>
<intProp name="ThreadGroup.ramp_time">300</intProp>
<boolProp name="ThreadGroup.scheduler">true</boolProp>
<stringProp name="ThreadGroup.duration">1800</stringProp>
<stringProp name="ThreadGroup.delay">0</stringProp>
<boolProp name="ThreadGroup.same_user_on_next_iteration">false</boolProp>
</ThreadGroup>
Use the Ultimate Thread Group plugin for complex ramp patterns:
Example pattern for a load test: | Start | Delay | Startup | Hold | Shutdown | |-------|-------|---------|-------|----------| | 25 | 0s | 60s | 300s | 30s | | 25 | 60s | 60s | 240s | 30s | | 25 | 120s | 60s | 180s | 30s | | 25 | 180s | 60s | 120s | 30s |
Always configure HTTP Request Defaults at the test plan level:
Protocol: https
Server Name: ${BASE_URL}
Port Number: ${PORT}
Content Encoding: UTF-8
Implementation: HttpClient4
Connect Timeout: 5000
Response Timeout: 30000
Extract values from JSON responses:
Variable Names: auth_token
JSON Path Expressions: $.token
Match No.: 1
Default Values: NOT_FOUND
For HTML or non-JSON responses:
Reference Name: csrf_token
Regular Expression: name="csrf_token" value="(.+?)"
Template: $1$
Match No.: 1
Default Value: NOT_FOUND
Simpler alternative to regex:
Reference Name: session_id
Left Boundary: sessionId=
Right Boundary: ;
Match No.: 1
In subsequent requests:
Header: Authorization: Bearer ${auth_token}
URL Path: /api/users/${user_id}
Body: {"sessionId": "${session_id}"}
Apply to: Main sample only
Field to Test: Response Code
Pattern Matching Rules: Equals
Patterns to Test: 200
Assert JSON Path exists: $.data.id
Expected Value: (leave empty to just check existence)
Additionally assert value: false
Duration in milliseconds: 2000
Apply to: Main sample only
Size to Assert: Response body
Type of Comparison: < (less than)
Size in bytes: 1048576
Thread Delay: 1000
More realistic than constant timers:
Deviation: 500
Constant Delay Offset: 2000
This produces delays between ~1000ms and ~3000ms with most around 2000ms.
Random Delay Maximum: 3000
Constant Delay Offset: 1000
Produces delays between 1000ms and 4000ms uniformly distributed.
Filename: data/users.csv
File Encoding: UTF-8
Variable Names: username,password,role
Ignore first line: true
Delimiter: ,
Allow quoted data: true
Recycle on EOF: true
Stop thread on EOF: false
Sharing mode: All threads
username,password,role
[email protected],Pass123!,user
[email protected],Pass456!,user
[email protected],Admin789!,admin
import java.time.Instant
import java.util.UUID
vars.put("request_id", UUID.randomUUID().toString())
vars.put("timestamp", Instant.now().toString())
vars.put("random_email", "user_${__Random(1000,9999)}@example.com")
// Generate random order amount
def amount = (Math.random() * 1000 + 10).round(2)
vars.put("order_amount", amount.toString())
import groovy.json.JsonSlurper
def response = prev.getResponseDataAsString()
def json = new JsonSlurper().parseText(response)
if (json.data && json.data.size() > 0) {
def firstItem = json.data[0]
vars.put("product_id", firstItem.id.toString())
vars.put("product_name", firstItem.name)
log.info("Extracted product: ${firstItem.name}")
} else {
log.warn("No products found in response")
prev.setSuccessful(false)
prev.setResponseMessage("No products in response")
}
import groovy.json.JsonSlurper
def response = prev.getResponseDataAsString()
def json = new JsonSlurper().parseText(response)
// Validate response structure
assert json.data != null : "Response missing 'data' field"
assert json.data.size() > 0 : "Data array is empty"
assert json.total >= json.data.size() : "Total count inconsistent"
// Validate each item
json.data.each { item ->
assert item.id != null : "Item missing ID"
assert item.name?.trim() : "Item missing name"
assert item.price > 0 : "Item price must be positive"
}
On the master machine (jmeter.properties):
remote_hosts=slave1:1099,slave2:1099,slave3:1099
server.rmi.ssl.disable=true
mode=StrippedBatch
On each slave machine:
# Start JMeter server
jmeter-server -Djava.rmi.server.hostname=<slave-ip>
Run distributed test:
jmeter -n -t test-plans/load-test.jmx \
-R slave1,slave2,slave3 \
-l results/distributed-results.jtl \
-e -o reports/distributed-report
# Basic run
jmeter -n -t test-plans/load-test.jmx -l results/results.jtl
# With properties override
jmeter -n -t test-plans/load-test.jmx \
-JBASE_URL=staging.example.com \
-JTHREADS=200 \
-JRAMPUP=300 \
-JDURATION=1800 \
-l results/results.jtl
# Generate HTML report after test
jmeter -g results/results.jtl -o reports/html-report
# Run with HTML report generation
jmeter -n -t test-plans/load-test.jmx \
-l results/results.jtl \
-e -o reports/html-report
# With specific log level
jmeter -n -t test-plans/load-test.jmx \
-l results/results.jtl \
-LDEBUG
For real-time monitoring with Grafana:
Backend Listener Implementation: org.apache.jmeter.visualizers.backend.influxdb.InfluxdbBackendListenerClient
influxdbUrl: http://influxdb:8086/write?db=jmeter
application: my-app
measurement: jmeter
summaryOnly: false
samplersRegex: .*
In jmeter.properties or user.properties:
jmeter.save.saveservice.output_format=csv
jmeter.save.saveservice.response_data=false
jmeter.save.saveservice.samplerData=false
jmeter.save.saveservice.requestHeaders=false
jmeter.save.saveservice.url=true
jmeter.save.saveservice.responseHeaders=false
jmeter.save.saveservice.timestamp_format=ms
jmeter.save.saveservice.successful=true
jmeter.save.saveservice.label=true
jmeter.save.saveservice.code=true
jmeter.save.saveservice.message=true
jmeter.save.saveservice.threadName=true
jmeter.save.saveservice.time=true
jmeter.save.saveservice.connect_time=true
jmeter.save.saveservice.latency=true
jmeter.save.saveservice.bytes=true
-Xms1g -Xmx4g for large load tests.development
Build WebdriverIO E2E suites — wdio.conf.ts setup, $ and $$ selectors, auto-wait and waitUntil, Mocha framework structure, page objects, parallel capabilities, and services for visual testing and Appium mobile.
testing
Test Vue 3 components with Vue Test Utils and Vitest — mount vs shallowMount, finding and triggering DOM, asserting props and emitted events, awaiting async updates, and mocking Pinia stores and Vue Router.
testing
Write fast unit and integration tests with Vitest — vitest.config.ts setup, vi.fn and vi.mock module mocking, fake timers, snapshots, V8 coverage with thresholds, workspaces for monorepos, and in-source testing.
development
Practice strict red-green-refactor test-driven development — write one failing test first, make it pass with the minimum code, then refactor under green, with worked cycles in Jest and pytest, AAA structure, and behavior-based test naming.