Time to put everything together. This is a hands-on exercise that walks you through building a complete parameterized and correlated login flow from scratch. Follow each step carefully -- this is exactly the process you would follow in a real project.
You will build a test that: (1) Reads user credentials from a CSV file, (2) Logs into a web application, extracting the CSRF token, (3) Captures the authentication token from the login response, (4) Makes an authenticated API call to fetch the user profile, (5) Verifies the profile name matches the expected name from the CSV file, (6) Runs with 5 threads, each using different credentials.
username,password,expected_name
user1@test.com,Password@1,User One
user2@test.com,Password@2,User Two
user3@test.com,Password@3,User Three
user4@test.com,Password@4,User Four
user5@test.com,Password@5,User FiveCreate a new Test Plan. Add a Thread Group with 5 threads, 10 second ramp-up, and 2 loop count
Add Config Elements: HTTP Cookie Manager (clear cookies each iteration = checked), HTTP Request Defaults (server name, port, protocol)
Add CSV Data Set Config as child of Thread Group. Set filename to test-data/login_users.csv, variable names to username,password,expected_name, ignore first line = true, sharing mode = All threads
Add HTTP Request: GET /login (to fetch the login page with CSRF token)
Right-click on GET /login > Add > Post Processors > Regular Expression Extractor. Name: csrf_token, Regex: name="_csrf" value="(.+?)", Template: $1$, Match No: 1, Default: CSRF_NOT_FOUND
Add HTTP Request: POST /api/login with body {"username":"${username}","password":"${password}","_csrf":"${csrf_token}"}
Right-click on POST /api/login > Add > Post Processors > JSON Extractor. Variable: auth_token, JSONPath: $.token, Default: TOKEN_NOT_FOUND
Add HTTP Header Manager (after the login request): Authorization = Bearer ${auth_token}
Add HTTP Request: GET /api/profile. This uses the auth token from the header manager
Right-click on GET /api/profile > Add > Assertions > Response Assertion. Pattern: ${expected_name}. This verifies the correct user is logged in
Add Debug Sampler after each extraction point (for debugging -- disable before load test)
Add View Results Tree listener for debugging, and Summary Report listener for metrics
Test Plan: Login Flow Exercise
├── User Defined Variables
│ ├── BASE_URL = your-app.example.com
│ ├── PROTOCOL = https
│ └── PORT = 443
│
└── Thread Group (5 threads, 10s ramp-up, 2 loops)
├── HTTP Cookie Manager
├── HTTP Request Defaults
│ (Server: ${BASE_URL}, Protocol: ${PROTOCOL}, Port: ${PORT})
├── CSV Data Set Config: login_users.csv
│
├── Transaction Controller: "Login Flow"
│ ├── GET /login
│ │ └── Regex Extractor: csrf_token
│ │ Regex: name="_csrf" value="(.+?)"
│ │ Default: CSRF_NOT_FOUND
│ ├── Debug Sampler (after CSRF extraction)
│ │
│ ├── POST /api/login
│ │ Body: {"username":"${username}","password":"${password}","_csrf":"${csrf_token}"}
│ │ └── JSON Extractor: auth_token
│ │ JSONPath: $.token
│ │ Default: TOKEN_NOT_FOUND
│ └── Debug Sampler (after login)
│
├── HTTP Header Manager
│ └── Authorization: Bearer ${auth_token}
│
├── Constant Timer: 2000ms
│
├── Transaction Controller: "Profile Verification"
│ ├── GET /api/profile
│ │ └── Response Assertion
│ │ Contains: ${expected_name}
│ └── Debug Sampler (after profile)
│
├── View Results Tree (disable for load test!)
└── Summary Report// Add as JSR223 Assertion under POST /api/login
// Validates the entire correlation chain before proceeding
def errors = []
// Check CSRF was extracted
def csrf = vars.get("csrf_token")
if (csrf == null || csrf == "CSRF_NOT_FOUND") {
errors.add("CSRF token not extracted from login page")
}
// Check auth token was returned
def token = vars.get("auth_token")
if (token == null || token == "TOKEN_NOT_FOUND") {
errors.add("Auth token not found in login response (code: ${prev.getResponseCode()})")
}
// Check CSV data is populated
def username = vars.get("username")
if (username == null || username.isEmpty()) {
errors.add("CSV username not populated -- check CSV Data Set Config")
}
if (!errors.isEmpty()) {
AssertionResult.setFailure(true)
AssertionResult.setFailureMessage(
"Correlation validation failed:\n" + errors.join("\n")
)
log.error("Thread ${ctx.getThread().getThreadName()}: ${errors.join(', ')}")
}Key Point: Always build and debug with 1 thread, then scale. Disable debug tools before load testing. Validate every extraction with Debug Samplers during development.
Key Point: Build the complete flow step by step: CSV for user data, extractors for CSRF and auth tokens, assertions for verification. Debug with 1 thread, then scale to full load.
Answer all 8 questions, then submit to see your score.
1. What is the primary purpose of correlation in performance testing?
2. Which extractor should you use for a REST API that returns JSON responses?
3. In the Regular Expression Extractor, what does the Template value "$1$" mean?
4. What is the correct JSONPath expression to extract the "token" field from {"auth":{"token":"abc123"}}?
5. In CSV Data Set Config, what does "Sharing mode: All threads" mean?
6. Where should a Post Processor (extractor) be placed in the JMeter test plan tree?
7. A recorded JMeter script returns 403 on every POST request during replay. What is the most likely cause?
8. Which JMeter function generates a unique identifier that is different for every call?