A comprehensive JMeter performance test plan for the Notes API (practice.expandtesting.com), covering user management, authentication, profile operations, and full CRUD lifecycle for notes.
- Overview
- Prerequisites
- File Structure
- Configuration
- Test Data Setup
- Test Flow
- API Endpoints Tested
- Assertions & SLA Thresholds
- Running the Test
- Reports & Output
- Customization
- Troubleshooting
This JMeter test plan simulates realistic user traffic against a Notes REST API. It performs:
- Health check validation before test execution
- User registration (conditional for new accounts)
- User login with token extraction
- Profile view and update operations
- Notes CRUD lifecycle (Create, Read, Update, Delete) across 10 iterations
The test uses CSV-driven data, Gaussian timers for realistic pacing, JSON assertions for response validation, and automatic token expiry handling.
| Requirement | Version |
|---|---|
| Apache JMeter | 5.6.3+ |
| Java (JDK/JRE) | 8+ |
Network access to practice.expandtesting.com:443 |
— |
C:/JMeter/test-data/ # Shared test data (master + slaves)
├── users.csv # User credentials (name, email, password, isNewAccount)
└── notes.csv # Note categories
swagger_note_api/ # Project folder
├── swagger_note_performance_tests.jmx # Main JMeter test plan
├── images/
│ ├── jmeter_structure.png # Test plan structure screenshot
│ ├── load_test_statistics.png # Load test statistics report
│ └── load_test_apdex.png # Load test APDEX score report
├── results/
│ ├── load_test_results.jtl
│ ├── LoadTestReport/
│ ├── stress_test_results.jtl
│ └── StressTestReport/
└── README.md
| Variable | Default Value | Description |
|---|---|---|
BASE_URL |
practice.expandtesting.com |
Target API hostname |
API_VERSION |
2.0.0 |
API version identifier |
| Parameter | Default | CLI Override |
|---|---|---|
| Threads (concurrent users) | 30 | -Jthreads=50 |
| Ramp-up period | 60s | -Jrampup=30 |
| Duration | 300s (5 min) | -Jduration=600 |
Note: Test data uses a fixed path (
C:/JMeter/test-data/) so slave PCs in a distributed setup can keep test data at the same location without needing to adjust for different PC names.
name,email,password,isNewAccount
John Doe,john@example.com,Pass123!,true
Jane Smith,jane@example.com,Pass456!,false| Column | Description |
|---|---|
name |
User's display name |
email |
Unique email address |
password |
Account password |
isNewAccount |
true = register before login; false = skip registration |
note_catgeory
work
personal
health
financeNote: CSV files are located at
C:/JMeter/test-data/. This fixed path is used intentionally so that in a master-slave (distributed) setup, all slave PCs can keep test data at the same location without needing to adjust for different PC names.
┌─────────────────────────────────────────────────────┐
│ PRE-TEST SETUP │
│ └── Health Check (once) │
│ └── GET /notes/api/health-check │
└─────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ MAIN TEST (30 threads, 5 min duration) │
│ │
│ 1. Load CSV data (users + notes) │
│ │
│ 2. UNAUTHENTICATED FLOW │
│ ├── [Conditional] POST /users/register │
│ │ └── Only if isNewAccount=true │
│ └── POST /users/login │
│ └── Extracts auth token │
│ │
│ 3. AUTHENTICATED FLOW │
│ ├── Profile Operations │
│ │ ├── GET /users/profile │
│ │ └── PATCH /users/profile │
│ │ │
│ └── Notes CRUD (10 iterations) │
│ ├── POST /notes/ → Create │
│ ├── GET /notes → Read all │
│ ├── PUT /notes/{id} → Update │
│ └── DELETE /notes/{id} → Delete │
│ │
│ 4. Token Expiry Handling │
│ └── JSR223 PostProcessor wipes token on 401 │
└─────────────────────────────────────────────────────┘
| Method | Endpoint | Purpose | Duration SLA |
|---|---|---|---|
GET |
/notes/api/health-check |
Verify API availability | < 200ms |
| Method | Endpoint | Purpose | Duration SLA | Auth Required |
|---|---|---|---|---|
POST |
/notes/api/users/register |
Register new user | — | No |
POST |
/notes/api/users/login |
Authenticate & get token | < 2000ms | No |
| Method | Endpoint | Purpose | Duration SLA |
|---|---|---|---|
GET |
/notes/api/users/profile |
View user profile | < 1000ms |
PATCH |
/notes/api/users/profile |
Update profile details | < 1000ms |
| Method | Endpoint | Purpose | Duration SLA |
|---|---|---|---|
POST |
/notes/api/notes/ |
Create a new note | < 2000ms |
GET |
/notes/api/notes |
Retrieve all notes | < 2000ms |
PUT |
/notes/api/notes/{id} |
Update a note | < 2000ms |
DELETE |
/notes/api/notes/{id} |
Delete a note | < 200ms |
All endpoints assert HTTP 200 OK responses.
| Endpoint Category | Max Duration |
|---|---|
| Health Check | 200ms |
| Login | 2000ms |
| Profile (GET/PATCH) | 1000ms |
| Notes (CREATE/READ/UPDATE) | 2000ms |
| Notes (DELETE) | 200ms |
| Field | Validation |
|---|---|
$.data.id |
NOT_NULL |
$.message |
NOT_NULL |
$.data.token (login) |
NOT_NULL |
$.data.name |
NOT_NULL |
$.data.email |
NOT_NULL |
$.data.title |
NOT_NULL |
$.data.description |
NOT_NULL |
$.data.category |
NOT_NULL |
$.data.user_id |
NOT_NULL |
jmeter -n -t swagger_note_performance_tests.jmx -l results.jtl# Load Test: 50 users, 30s ramp-up, 5 min duration
jmeter -n -t swagger_note_performance_tests.jmx -Jthreads=50 -Jrampup=30 -Jduration=300 -l results/load_test_results.jtl -e -o results/LoadTestReport
# Stress Test: 100 users, 120s ramp-up, 10 min duration
jmeter -n -t swagger_note_performance_tests.jmx -Jthreads=100 -Jrampup=120 -Jduration=600 -l results/stress_test_results.jtl -e -o results/StressTestReportAll reports are saved to the results/ folder (PC-specific path).
| Report | Filename | Contents |
|---|---|---|
| Assertion Results | results/assertion_report.csv |
Pass/fail status for each assertion |
| Summary Report | results/summary_report.csv |
Aggregate stats (avg, min, max, throughput) |
| View Results Tree | results/tree-report.csv |
Detailed request/response data |
| Load Test JTL | results/load_test_results.jtl |
Raw sample results (load) |
| Load Test HTML | results/LoadTestReport/ |
Interactive dashboard (load) |
| Stress Test JTL | results/stress_test_results.jtl |
Raw sample results (stress) |
| Stress Test HTML | results/StressTestReport/ |
Interactive dashboard (stress) |
# From existing JTL file
jmeter -g results/load_test_results.jtl -o results/LoadTestReportStatistics Report:
APDEX Score Report:
Edit the BASE_URL variable in the test plan, or override via CLI using -J flags (see Parameterized Run).
Update CSV files at C:/JMeter/test-data/:
- users.csv — Add/remove user credentials
- notes.csv — Change note categories
Note: Test data uses a fixed path (
C:/JMeter/test-data/) so slave PCs in a distributed setup can replicate the same structure without path conflicts.
| Parameter | Location | Description |
|---|---|---|
| Thread count | Thread Group | Number of concurrent users |
| Ramp-up | Thread Group | Time to spin up all threads |
| Duration | Thread Group | Total test execution time |
| Loop count | Loop Controller | Iterations per thread (Notes CRUD = 10) |
The JSR223 PostProcessor automatically detects 401 Unauthorized responses and clears the stored token, forcing re-authentication on the next request.
// Located in: Simple Controller - [Authenticated]
// Triggers re-login when token expires
if (prev.getResponseCode() == "401") {
vars.put("token", "");
}| Issue | Cause | Solution |
|---|---|---|
TOKEN_NOT_FOUND |
Login failed or CSV mismatch | Verify users.csv credentials |
NOTE_ID_NOT_FOUND |
Create Note failed | Check API availability and request body |
| High response times | Server overload or network latency | Reduce thread count or increase ramp-up |
Connection refused |
Firewall or incorrect BASE_URL | Verify network access to practice.expandtesting.com:443 |
| Registration failures | Duplicate email in CSV | Ensure unique emails for isNewAccount=true users |
This test plan is provided as-is for performance testing and educational purposes.


