perf-skills 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +8 -0
- package/README.md +227 -0
- package/package.json +42 -0
- package/perf-skills/.claude-plugin/plugin.json +5 -0
- package/perf-skills/SKILL.md +169 -0
- package/perf-skills/references/tools/gatling.md +286 -0
- package/perf-skills/references/tools/jmeter.md +251 -0
- package/perf-skills/references/tools/k6.md +269 -0
- package/perf-skills/references/tools/loadrunner.md +116 -0
- package/perf-skills/references/tools/locust.md +133 -0
- package/perf-skills/references/tools/neoload.md +61 -0
- package/perf-skills/references/tools/octoperf.md +50 -0
- package/perf-skills/references/topics/database-testing.md +214 -0
- package/perf-skills/references/topics/modern-architectures.md +314 -0
- package/perf-skills/references/topics/observability.md +201 -0
- package/perf-skills/references/topics/production-testing.md +214 -0
- package/perf-skills/references/topics/protocol-testing.md +274 -0
- package/perf-skills/references/topics/results-analysis.md +244 -0
- package/perf-skills/references/topics/script-generation.md +221 -0
- package/perf-skills/references/topics/test-data.md +196 -0
- package/perf-skills/references/topics/test-execution.md +281 -0
- package/perf-skills/references/topics/workload-design.md +207 -0
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
# Gatling Reference
|
|
2
|
+
|
|
3
|
+
> Targets: Gatling 3.10+, Java DSL 3.7+
|
|
4
|
+
|
|
5
|
+
Gatling is a high-performance, Scala/Java-based load testing tool designed for HTTP-heavy applications. Its simulation DSL is expressive, and its async Netty-based engine handles very high concurrency with low resource overhead.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Core Concepts
|
|
10
|
+
|
|
11
|
+
| Concept | Description |
|
|
12
|
+
|---|---|
|
|
13
|
+
| **Simulation** | Top-level test class extending `Simulation` |
|
|
14
|
+
| **Protocol** | HTTP, JMS, or gRPC configuration |
|
|
15
|
+
| **Scenario** | Named sequence of actions (chain of steps) |
|
|
16
|
+
| **Feeder** | Data source for parameterization (CSV, JSON, JDBC) |
|
|
17
|
+
| **Check** | Assertion on a response (status, body, header) |
|
|
18
|
+
| **Population** | VU injection strategy (open/closed model) |
|
|
19
|
+
| **Session** | VU-level state store (like JMeter `vars`) |
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Simulation Structure
|
|
24
|
+
|
|
25
|
+
```scala
|
|
26
|
+
import io.gatling.core.Predef._
|
|
27
|
+
import io.gatling.http.Predef._
|
|
28
|
+
import scala.concurrent.duration._
|
|
29
|
+
|
|
30
|
+
class CheckoutSimulation extends Simulation {
|
|
31
|
+
|
|
32
|
+
// 1. Protocol config
|
|
33
|
+
val httpProtocol = http
|
|
34
|
+
.baseUrl("https://api.example.com")
|
|
35
|
+
.acceptHeader("application/json")
|
|
36
|
+
.contentTypeHeader("application/json")
|
|
37
|
+
.shareConnections // Reuse connections across VUs (more realistic)
|
|
38
|
+
|
|
39
|
+
// 2. Feeders (data)
|
|
40
|
+
val userFeeder = csv("data/users.csv").circular
|
|
41
|
+
|
|
42
|
+
// 3. Scenario (chain of steps)
|
|
43
|
+
val loginScenario = scenario("Login and Browse")
|
|
44
|
+
.feed(userFeeder)
|
|
45
|
+
.exec(
|
|
46
|
+
http("POST Login")
|
|
47
|
+
.post("/auth/login")
|
|
48
|
+
.body(StringBody("""{"username":"#{username}","password":"#{password}"}""")).asJson
|
|
49
|
+
.check(status.is(200))
|
|
50
|
+
.check(jsonPath("$.token").saveAs("authToken"))
|
|
51
|
+
)
|
|
52
|
+
.pause(1, 3) // Think time: 1–3 seconds
|
|
53
|
+
.exec(
|
|
54
|
+
http("GET Products")
|
|
55
|
+
.get("/products")
|
|
56
|
+
.header("Authorization", "Bearer #{authToken}")
|
|
57
|
+
.check(status.is(200))
|
|
58
|
+
.check(jsonPath("$[0].id").saveAs("productId"))
|
|
59
|
+
)
|
|
60
|
+
.pause(2)
|
|
61
|
+
|
|
62
|
+
// 4. Injection (load profile)
|
|
63
|
+
setUp(
|
|
64
|
+
loginScenario.inject(
|
|
65
|
+
nothingFor(5.seconds), // Wait before starting
|
|
66
|
+
atOnceUsers(5), // Smoke check
|
|
67
|
+
rampUsers(100).during(2.minutes),
|
|
68
|
+
constantUsersPerSec(20).during(5.minutes),
|
|
69
|
+
rampUsersPerSec(20).to(0).during(1.minute)
|
|
70
|
+
).protocols(httpProtocol)
|
|
71
|
+
).assertions(
|
|
72
|
+
global.responseTime.percentile(95).lt(500),
|
|
73
|
+
global.failedRequests.percent.lt(1)
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Injection Profiles
|
|
81
|
+
|
|
82
|
+
### Closed model (VU-based)
|
|
83
|
+
```scala
|
|
84
|
+
rampUsers(100).during(2.minutes) // Ramp to 100 VUs over 2 min
|
|
85
|
+
constantConcurrentUsers(100).during(5.minutes) // Hold at 100 VUs
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Open model (arrival-rate)
|
|
89
|
+
```scala
|
|
90
|
+
constantUsersPerSec(50).during(5.minutes) // 50 new users/sec
|
|
91
|
+
rampUsersPerSec(10).to(100).during(3.minutes) // Ramp RPS from 10 to 100
|
|
92
|
+
heavisideUsers(1000).during(20.seconds) // S-curve injection (realistic burst)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
> For closed vs open model guidance and when to use each, see `references/topics/workload-design.md`.
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Feeders (Parameterization)
|
|
100
|
+
|
|
101
|
+
```scala
|
|
102
|
+
// CSV
|
|
103
|
+
val csvFeeder = csv("data/users.csv").circular // Loop forever
|
|
104
|
+
val csvFeeder = csv("data/users.csv").random // Pick random row
|
|
105
|
+
val csvFeeder = csv("data/users.csv").shuffle // Randomize order, no repeat
|
|
106
|
+
val csvFeeder = csv("data/users.csv").queue // Each VU gets next row; fail if empty
|
|
107
|
+
|
|
108
|
+
// JSON
|
|
109
|
+
val jsonFeeder = jsonFile("data/products.json").random
|
|
110
|
+
|
|
111
|
+
// Custom feeder
|
|
112
|
+
val customFeeder = Iterator.continually(Map(
|
|
113
|
+
"timestamp" -> System.currentTimeMillis(),
|
|
114
|
+
"uuid" -> java.util.UUID.randomUUID().toString
|
|
115
|
+
))
|
|
116
|
+
|
|
117
|
+
// Use in scenario
|
|
118
|
+
scenario("test").feed(csvFeeder).exec(...)
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Checks (Assertions)
|
|
124
|
+
|
|
125
|
+
```scala
|
|
126
|
+
// Status code
|
|
127
|
+
.check(status.is(200))
|
|
128
|
+
.check(status.in(200, 201))
|
|
129
|
+
|
|
130
|
+
// JSON body
|
|
131
|
+
.check(jsonPath("$.userId").exists)
|
|
132
|
+
.check(jsonPath("$.token").saveAs("token"))
|
|
133
|
+
.check(jsonPath("$.items.length()").is("5"))
|
|
134
|
+
|
|
135
|
+
// Header
|
|
136
|
+
.check(header("X-Request-Id").exists)
|
|
137
|
+
|
|
138
|
+
// Response time
|
|
139
|
+
.check(responseTimeInMillis.lte(2000))
|
|
140
|
+
|
|
141
|
+
// Body string
|
|
142
|
+
.check(bodyString.contains("success"))
|
|
143
|
+
.check(substring("access_granted").count.is(1))
|
|
144
|
+
|
|
145
|
+
// Multiple checks (all must pass)
|
|
146
|
+
.check(status.is(200), jsonPath("$.status").is("ok"))
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Session Variables
|
|
152
|
+
|
|
153
|
+
```scala
|
|
154
|
+
// Save during check
|
|
155
|
+
.check(jsonPath("$.orderId").saveAs("orderId"))
|
|
156
|
+
|
|
157
|
+
// Access in body
|
|
158
|
+
.body(StringBody("""{"orderId":"#{orderId}"}"""))
|
|
159
|
+
|
|
160
|
+
// Access in Exec block
|
|
161
|
+
.exec(session => {
|
|
162
|
+
val orderId = session("orderId").as[String]
|
|
163
|
+
println(s"Processing order: $orderId")
|
|
164
|
+
session
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
// Conditional logic
|
|
168
|
+
.doIf(session => session("isAdmin").as[Boolean]) {
|
|
169
|
+
exec(http("Admin Action").get("/admin"))
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Assertions (Global SLA)
|
|
176
|
+
|
|
177
|
+
```scala
|
|
178
|
+
setUp(...).assertions(
|
|
179
|
+
global.responseTime.percentile(95).lt(500), // p95 < 500ms
|
|
180
|
+
global.responseTime.max.lt(2000), // max < 2s
|
|
181
|
+
global.failedRequests.percent.lt(1), // Error rate < 1%
|
|
182
|
+
global.requestsPerSec.gte(100), // Throughput >= 100 RPS
|
|
183
|
+
forAll.failedRequests.count.is(0), // No failures on any request
|
|
184
|
+
details("POST Login").responseTime.percentile(99).lt(1000) // Named request SLA
|
|
185
|
+
)
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## Protocol Configuration
|
|
191
|
+
|
|
192
|
+
### HTTP
|
|
193
|
+
```scala
|
|
194
|
+
val httpProtocol = http
|
|
195
|
+
.baseUrl("https://api.example.com")
|
|
196
|
+
.proxy(Proxy("proxy.corp.net", 8080))
|
|
197
|
+
.acceptHeader("application/json")
|
|
198
|
+
.acceptEncodingHeader("gzip, deflate")
|
|
199
|
+
.userAgentHeader("Gatling/Perf-Test")
|
|
200
|
+
.maxConnectionsPerHost(10)
|
|
201
|
+
.warmUp("https://api.example.com/health")
|
|
202
|
+
.disableFollowRedirect // Disable for accuracy
|
|
203
|
+
.disableAutoReferer
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### WebSocket
|
|
207
|
+
```scala
|
|
208
|
+
import io.gatling.http.Predef._
|
|
209
|
+
|
|
210
|
+
exec(
|
|
211
|
+
ws("Connect WS").connect("/ws/chat")
|
|
212
|
+
.await(5.seconds)(
|
|
213
|
+
ws.checkTextMessage("message received")
|
|
214
|
+
.matching(jsonPath("$.type").is("ack"))
|
|
215
|
+
)
|
|
216
|
+
)
|
|
217
|
+
.exec(ws("Send Message").sendText("""{"msg":"hello"}"""))
|
|
218
|
+
.exec(ws("Close").close())
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Running Gatling
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
# Maven (Scala DSL)
|
|
227
|
+
mvn gatling:test -Dgatling.simulationClass=CheckoutSimulation
|
|
228
|
+
|
|
229
|
+
# Gradle (Java DSL)
|
|
230
|
+
./gradlew gatlingRun
|
|
231
|
+
|
|
232
|
+
# Bundle (no build tool)
|
|
233
|
+
./bin/gatling.sh -s CheckoutSimulation -rd "Staging load test"
|
|
234
|
+
|
|
235
|
+
# With overrides
|
|
236
|
+
mvn gatling:test \
|
|
237
|
+
-Dgatling.simulationClass=CheckoutSimulation \
|
|
238
|
+
-DbaseUrl=https://staging.example.com \
|
|
239
|
+
-Dusers=100 \
|
|
240
|
+
-Dduration=300
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## Java DSL (Gatling 3.7+)
|
|
246
|
+
|
|
247
|
+
Gatling now supports Java and Kotlin natively — no Scala required:
|
|
248
|
+
|
|
249
|
+
```java
|
|
250
|
+
import io.gatling.javaapi.core.*;
|
|
251
|
+
import io.gatling.javaapi.http.*;
|
|
252
|
+
import static io.gatling.javaapi.core.CoreDsl.*;
|
|
253
|
+
import static io.gatling.javaapi.http.HttpDsl.*;
|
|
254
|
+
|
|
255
|
+
public class LoginSimulation extends Simulation {
|
|
256
|
+
HttpProtocolBuilder httpProtocol = http.baseUrl("https://api.example.com");
|
|
257
|
+
|
|
258
|
+
ScenarioBuilder scenario = scenario("Login")
|
|
259
|
+
.exec(
|
|
260
|
+
http("POST Login")
|
|
261
|
+
.post("/auth/login")
|
|
262
|
+
.body(StringBody("{\"user\":\"test\"}")).asJson()
|
|
263
|
+
.check(status().is(200))
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
{ setUp(scenario.injectOpen(rampUsers(100).during(60))).protocols(httpProtocol); }
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## Results
|
|
273
|
+
|
|
274
|
+
Gatling generates an HTML report in `target/gatling/<simulation-timestamp>/index.html` — commit the link to CI artifacts or use Gatling Enterprise for centralized dashboards.
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## Gatling-Specific Tips
|
|
279
|
+
|
|
280
|
+
- **Never use blocking/synchronous calls inside `exec`** — Gatling's engine is async; blocking calls degrade throughput significantly.
|
|
281
|
+
- **Use `.warmUp()` or an initial ramp phase** — JVM JIT compilation distorts early metrics without warm-up.
|
|
282
|
+
- **Use `global` or named `details()` assertions** — asserting on individual requests instead of aggregated transactions is noisy.
|
|
283
|
+
- **Prefer `heavisideUsers` for burst injection** — S-curve injection is more realistic than `atOnceUsers` for spike tests.
|
|
284
|
+
|
|
285
|
+
> For CI/CD integration (Maven, Gradle, GitHub Actions), see `references/topics/test-execution.md`.
|
|
286
|
+
> For anti-patterns, assertions, think time, and parameterization principles, see **Key Principles** in `SKILL.md`.
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
# JMeter Reference
|
|
2
|
+
|
|
3
|
+
> Targets: JMeter 5.6+, Plugins Manager 1.10+
|
|
4
|
+
|
|
5
|
+
Apache JMeter is the most widely used open-source performance testing tool. It supports a broad range of protocols and is extended through a rich plugin ecosystem.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Core Concepts
|
|
10
|
+
|
|
11
|
+
| Concept | Description |
|
|
12
|
+
|---|---|
|
|
13
|
+
| **Test Plan** | Root container — the `.jmx` file |
|
|
14
|
+
| **Thread Group** | Defines VU count, ramp-up, and loop count |
|
|
15
|
+
| **Sampler** | Makes a request (HTTP, JDBC, JMS, TCP, etc.) |
|
|
16
|
+
| **Controller** | Logic: Loop, If, While, Throughput |
|
|
17
|
+
| **Config Element** | HTTP Defaults, CSV Data Set, Cache Manager |
|
|
18
|
+
| **Pre/Post Processor** | Run before/after a sampler (BeanShell, JSR223) |
|
|
19
|
+
| **Assertion** | Validates the response (Response Code, Duration, JSON) |
|
|
20
|
+
| **Listener** | Collects and displays results (View Results Tree, Summary) |
|
|
21
|
+
| **Timer** | Adds think time (Constant, Gaussian, Synchronizing) |
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Thread Group Configuration Patterns
|
|
26
|
+
|
|
27
|
+
### Standard load ramp
|
|
28
|
+
```
|
|
29
|
+
Thread Group:
|
|
30
|
+
Number of Threads (users): 100
|
|
31
|
+
Ramp-Up Period (seconds): 120 ← 1 VU every 1.2s, avoids thundering herd
|
|
32
|
+
Loop Count: -1 ← run forever until duration
|
|
33
|
+
Duration: 600 ← 10 minutes
|
|
34
|
+
Startup Delay: 0
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Stepping Thread Group (requires JMeter Plugins)
|
|
38
|
+
Use `jp@gc - Stepping Thread Group` for staged load:
|
|
39
|
+
- Start 10 VUs, hold 60s → add 10 every 30s → max 100 VUs → hold 300s → ramp down
|
|
40
|
+
|
|
41
|
+
### Concurrency Thread Group (preferred for modern tests)
|
|
42
|
+
Use `jp@gc - Concurrency Thread Group` for target-concurrency model:
|
|
43
|
+
- Maintains a target concurrency level dynamically, compensates for slow VUs
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## HTTP Sampler Best Practices
|
|
48
|
+
|
|
49
|
+
- Always use **HTTP Request Defaults** config element for base URL/port/protocol — never hardcode in individual samplers.
|
|
50
|
+
- Set **Content-Type** header in an HTTP Header Manager at Thread Group level, not per sampler.
|
|
51
|
+
- Use **KeepAlive** (default on) for realistic connection reuse.
|
|
52
|
+
- Use **Follow Redirects** only when your app requires it; disable otherwise for accuracy.
|
|
53
|
+
- Avoid using **View Results Tree** listener in load tests — it's memory-intensive; use it only during script development.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Correlation: Extracting Dynamic Values
|
|
58
|
+
|
|
59
|
+
Correlation is the #1 cause of script failures in session-heavy apps.
|
|
60
|
+
|
|
61
|
+
### Regular Expression Extractor
|
|
62
|
+
```
|
|
63
|
+
Reference Name: csrf_token
|
|
64
|
+
Regular Expression: name="_token" value="(.+?)"
|
|
65
|
+
Template: $1$
|
|
66
|
+
Match No.: 1
|
|
67
|
+
Default Value: NOT_FOUND
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### JSON Extractor (REST APIs)
|
|
71
|
+
```
|
|
72
|
+
Reference Name: access_token
|
|
73
|
+
JSON Path: $.data.token
|
|
74
|
+
Match No.: 1
|
|
75
|
+
Default Value: NOT_FOUND
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Boundary Extractor (fastest, no regex overhead)
|
|
79
|
+
```
|
|
80
|
+
Reference Name: session_id
|
|
81
|
+
Left Boundary: "sessionId":"
|
|
82
|
+
Right Boundary: "
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Using Extracted Values
|
|
86
|
+
Reference with `${variable_name}` in subsequent requests.
|
|
87
|
+
|
|
88
|
+
Always add an assertion on the extracted value:
|
|
89
|
+
```
|
|
90
|
+
Response Assertion → Variable: ${csrf_token} → Pattern: NOT_FOUND → Negate
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## CSV Data Set Config
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
Filename: ${__P(data.dir,./data)}/users.csv
|
|
99
|
+
Variable Names: username,password,account_id
|
|
100
|
+
Delimiter: ,
|
|
101
|
+
Allow Quoted Data: true
|
|
102
|
+
Recycle on EOF: true
|
|
103
|
+
Stop Thread on EOF: false
|
|
104
|
+
Sharing Mode: All Threads ← or "Current Thread Group" if isolated data needed
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**Tips:**
|
|
108
|
+
- Use `${__P(data.dir,...)}` property for portable paths across environments.
|
|
109
|
+
- For high VU counts, ensure the CSV has at least as many rows as peak VU count to avoid data collisions.
|
|
110
|
+
- In distributed tests, the CSV file must exist on **each injector node**, not just the controller.
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## JSR223 Scripting (Groovy)
|
|
115
|
+
|
|
116
|
+
Always use **JSR223** over BeanShell — Groovy is compiled and cached, BeanShell is not.
|
|
117
|
+
|
|
118
|
+
### Pre-processor: Generate a dynamic timestamp
|
|
119
|
+
```groovy
|
|
120
|
+
import java.time.Instant
|
|
121
|
+
vars.put("timestamp", Instant.now().toEpochMilli().toString())
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Post-processor: Parse JSON response
|
|
125
|
+
```groovy
|
|
126
|
+
import groovy.json.JsonSlurper
|
|
127
|
+
def json = new JsonSlurper().parseText(prev.getResponseDataAsString())
|
|
128
|
+
vars.put("userId", json.data.id.toString())
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Pre-processor: Compute HMAC signature
|
|
132
|
+
```groovy
|
|
133
|
+
import javax.crypto.Mac
|
|
134
|
+
import javax.crypto.spec.SecretKeySpec
|
|
135
|
+
def key = vars.get("api_secret")
|
|
136
|
+
def data = vars.get("request_body")
|
|
137
|
+
def mac = Mac.getInstance("HmacSHA256")
|
|
138
|
+
mac.init(new SecretKeySpec(key.bytes, "HmacSHA256"))
|
|
139
|
+
vars.put("signature", mac.doFinal(data.bytes).encodeHex().toString())
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Assertions
|
|
145
|
+
|
|
146
|
+
### Response Code Assertion
|
|
147
|
+
```
|
|
148
|
+
Field to Test: Response Code
|
|
149
|
+
Pattern: 200
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### JSON Assertion
|
|
153
|
+
```
|
|
154
|
+
JSON Path: $.status
|
|
155
|
+
Expected Value: success
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Duration Assertion
|
|
159
|
+
```
|
|
160
|
+
Duration to assert: 2000 ← flag any response > 2000ms
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Response Size Assertion
|
|
164
|
+
Use to detect incomplete responses — flag if body < 100 bytes unexpectedly.
|
|
165
|
+
|
|
166
|
+
**Important:** Add assertions to the **transaction controller level** where possible, not individual samplers, to get meaningful business-transaction-level validation.
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Transaction Controllers
|
|
171
|
+
|
|
172
|
+
Wrap related HTTP samplers in Transaction Controllers to measure end-to-end business transaction time:
|
|
173
|
+
|
|
174
|
+
```
|
|
175
|
+
Transaction Controller: Login Flow
|
|
176
|
+
├── HTTP Sampler: GET /login
|
|
177
|
+
├── HTTP Sampler: POST /authenticate
|
|
178
|
+
└── HTTP Sampler: GET /dashboard
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Set **Generate Parent Sample = true** to log only the aggregate transaction (not individual child requests) in results.
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Timers (Think Time)
|
|
186
|
+
|
|
187
|
+
| Timer | Use Case |
|
|
188
|
+
|---|---|
|
|
189
|
+
| Constant Timer | Simple fixed think time |
|
|
190
|
+
| Uniform Random Timer | Range between min–max |
|
|
191
|
+
| Gaussian Random Timer | Bell-curve distribution — most realistic |
|
|
192
|
+
| Throughput Shaping Timer | Target a specific RPS regardless of VU count |
|
|
193
|
+
|
|
194
|
+
Gaussian formula: `delay = Constant + Gaussian(deviation)`
|
|
195
|
+
Realistic web-app think time: Gaussian with constant=3000ms, deviation=1500ms.
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Running JMeter Non-GUI (Command Line)
|
|
200
|
+
|
|
201
|
+
> For distributed testing architecture and setup, see `references/topics/test-execution.md`.
|
|
202
|
+
|
|
203
|
+
Always run load tests in non-GUI mode:
|
|
204
|
+
```bash
|
|
205
|
+
jmeter -n \
|
|
206
|
+
-t test.jmx \
|
|
207
|
+
-l results/results.jtl \
|
|
208
|
+
-e -o results/dashboard \
|
|
209
|
+
-Jenv=staging \
|
|
210
|
+
-Jthreads=100 \
|
|
211
|
+
-Jduration=600
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
| Flag | Purpose |
|
|
215
|
+
|---|---|
|
|
216
|
+
| `-n` | Non-GUI mode |
|
|
217
|
+
| `-t` | Test plan path |
|
|
218
|
+
| `-l` | JTL results file |
|
|
219
|
+
| `-e -o` | Generate HTML dashboard after run |
|
|
220
|
+
| `-J` | Set JMeter property (use `${__P(env,dev)}` in plan) |
|
|
221
|
+
| `-G` | Set global property (available to remote engines) |
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## Key Plugins (JMeter Plugins Manager)
|
|
226
|
+
|
|
227
|
+
| Plugin | Purpose |
|
|
228
|
+
|---|---|
|
|
229
|
+
| `Concurrency Thread Group` | Maintain target concurrency |
|
|
230
|
+
| `Stepping Thread Group` | Step-ramp load profile |
|
|
231
|
+
| `Throughput Shaping Timer` | Control RPS exactly |
|
|
232
|
+
| `PerfMon` | Collect server-side CPU/memory via agent |
|
|
233
|
+
| `3 Basic Graphs` | Lightweight real-time charting |
|
|
234
|
+
| `JDBC Connection Configuration` | Database load testing |
|
|
235
|
+
| `WebSocket Sampler` | WS protocol support |
|
|
236
|
+
| `gRPC Sampler` | gRPC protocol support |
|
|
237
|
+
|
|
238
|
+
Install plugins via: **Options → Plugins Manager → Available Plugins**
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## JMeter-Specific Tips
|
|
243
|
+
|
|
244
|
+
- **Always run in non-GUI mode** for load tests — GUI mode consumes JMeter's own resources and distorts results.
|
|
245
|
+
- **Use HTTP Request Defaults** — hardcoded hosts make environment switching painful.
|
|
246
|
+
- **Never put listeners inside loops** — View Results Tree in a loop will OOM the JVM.
|
|
247
|
+
- **Use JSR223 (Groovy) over BeanShell** — Groovy is compiled and cached; BeanShell is interpreted per invocation.
|
|
248
|
+
- **Set JVM heap** for large tests: `JVM_ARGS="-Xms2g -Xmx4g" jmeter -n -t test.jmx`
|
|
249
|
+
|
|
250
|
+
> For CI/CD integration (Maven, GitHub Actions, GitLab, Jenkins), see `references/topics/test-execution.md`.
|
|
251
|
+
> For anti-patterns, assertions, think time, and parameterization principles, see **Key Principles** in `SKILL.md`.
|