On this page10 sections

JMeter API Load Test

Design and document a JMeter load test for a set of REST API endpoints — covering thread group configuration, HTTP requests, assertions, timers, CSV test data, and CLI execution.

Role

Performance QA

Difficulty

Intermediate

Time limit

120 min

Category

performance

JMeter

Scenario

Your team is preparing to load test the JSONPlaceholder API (https://jsonplaceholder.typicode.com) as a practice target before applying the same approach to a production-like system. You have been asked to design a JMeter test plan that simulates realistic API traffic across three endpoints: GET /users, POST /posts, and GET /posts/{id}. The plan should be executable from the command line, produce a result summary, and be documented well enough for another engineer to run it.

Requirements

  • 1.Design a JMeter test plan (.jmx outline or documented configuration) with a Thread Group configured for 50 concurrent users, 30-second ramp-up, and 2-minute steady-state duration
  • 2.Add three HTTP Request samplers: GET /users, POST /posts (with a JSON body containing userId, title, and body), and GET /posts/{id} — using a variable from the CSV data feeder for the post ID
  • 3.Configure HTTP Header Manager with Content-Type: application/json and, where applicable, an Authorization header placeholder
  • 4.Add a Response Assertion to each sampler: assert the expected HTTP status code and at least one key field in the response body (e.g. assert 'id' field is present in the POST /posts response)
  • 5.Add a Constant Timer (300 ms) or Gaussian Random Timer to simulate realistic think time between requests
  • 6.Set up a CSV Data Set Config to supply a list of post IDs for parameterising the GET /posts/{id} request — provide a sample CSV with at least five rows
  • 7.Document the CLI run command (non-GUI mode) using JMeter's -n flag, specifying the .jmx file, log file, and results file (.jtl)
  • 8.Interpret a sample results summary: report P50, P90, P95 response times, throughput (req/s), and error rate from a representative run — identify whether the results meet a P95 ≤ 1 s threshold

Starter data

  • Practice API: https://jsonplaceholder.typicode.com (public, no auth required, mutations are faked — returns correct status codes and shapes but does not persist data)
  • Endpoints: GET /users (returns array of 10 user objects), POST /posts (body: { userId, title, body }; returns 201 with the created object + auto-generated id), GET /posts/{id} (returns single post or 404 for id > 100)
  • Sample POST body: { "userId": 1, "title": "Load test post", "body": "Generated during load test" }
  • Sample CSV for post IDs: id\n1\n5\n10\n42\n99 — use JMeter CSV Data Set Config with variable name 'postId' and sharing mode 'All threads'
  • JMeter CLI reference: jmeter -n -t test-plan.jmx -l results.jtl -e -o report-output/

Expected deliverables

  • A documented JMeter test plan outline: Thread Group settings (users, ramp-up, duration), sampler list, timer configuration, assertion rules, and CSV feeder setup — in Markdown, a table, or annotated .jmx snippet
  • A sample CSV data file (postIds.csv) with at least five post ID rows and a header
  • The CLI run command with all required flags, documented with a brief explanation of each flag
  • A results interpretation section: P50, P90, P95 response times, throughput (req/s), and error rate from a representative run or a plausible simulated result set — with a pass/fail verdict against P95 ≤ 1 s
  • A README-style notes section: how to set the base URL via a JMeter property, how to change thread count without editing the .jmx, and any known caveats about the practice API (e.g. mutations are faked)

Evaluation rubric

DimensionWhat reviewers look for
Realistic thread group and ramp designIs the ramp-up period reasonable relative to the thread count — long enough for the application to stabilise (30 s for 50 threads gives 1.7 threads/s, which is sensible)? Is the steady-state duration long enough to collect statistically meaningful samples (at least 1 minute)? Setting ramp-up to 0 (all threads start simultaneously) is a spike test, not a load test — flag this if present.
Correct request and assertion setupAre HTTP method, URL, headers, and body correct for each endpoint? Does the POST sampler include Content-Type: application/json and a valid JSON body? Are assertions specific enough to catch regressions — 'response code 201' and 'response body contains id' is good; 'response is not empty' is too weak.
CSV data strategyIs the CSV Data Set Config configured correctly: file path, delimiter, variable name, sharing mode, and recycle behaviour? Does the parameterised request actually use the ${postId} variable in the URL path? Is the CSV sized appropriately for the number of threads and iterations (at least as many rows as threads to avoid collision)?
Non-GUI execution awarenessIs the CLI run command complete and correct: -n (non-GUI), -t (test plan), -l (log/results file)? Does the candidate understand that the JMeter GUI should not be used for actual load runs (it adds significant overhead)? Is the output format (.jtl) suitable for further analysis or HTML report generation (-e -o flags)?
Result interpretationAre P50, P90, P95 read correctly from the results (not confused with mean or max)? Is throughput expressed in requests/second (not requests/minute)? Does the pass/fail verdict correctly apply the stated threshold? Does the candidate distinguish between API-level errors (4xx/5xx) and JMeter-level errors (connection refused, timeout)?

Sample solution outline

  • Thread Group: 50 threads, ramp-up 30 s, loop count: duration 120 s (scheduler mode), 1 iteration per thread per loop — total expected samples ≈ 50 threads × (120 s / avg response time)
  • Sampler 1 — GET /users: GET https://jsonplaceholder.typicode.com/users; assert status 200; assert response body contains '"id"'; Constant Timer 300 ms after
  • Sampler 2 — POST /posts: POST https://jsonplaceholder.typicode.com/posts; body {"userId":1,"title":"test","body":"load run"}; header Content-Type: application/json; assert status 201; assert body contains '"id"'
  • Sampler 3 — GET /posts/{id}: GET https://jsonplaceholder.typicode.com/posts/${postId}; CSV Data Set Config supplies postId from postIds.csv; assert status 200 OR 404 (document both as expected for practice API)
  • CSV file: postIds.csv — header 'id', rows: 1, 5, 10, 42, 99; sharing mode 'All threads'; recycle on EOF = true; stop thread on EOF = false
  • CLI command: jmeter -n -t checkout-load.jmx -l results.jtl -e -o ./html-report (generates both raw .jtl and an HTML dashboard in ./html-report/)
  • Sample result interpretation: P50 = 185 ms, P90 = 340 ms, P95 = 480 ms — PASS (threshold P95 ≤ 1 s); throughput = 28.4 req/s; error rate = 0.0% (JSONPlaceholder returns 404 for id > 100 but these are expected and not counted as errors if assertion allows 404)
  • README notes: override base URL with -Jbase_url=https://other-host.com on CLI; change thread count with -Jthreads=100; JSONPlaceholder POST responses have id > 200 (fake increment) — do not assert id range

Common mistakes

  • Setting ramp-up to 0 seconds — this creates a spike test, not a steady load test; all threads hit the server simultaneously and the initial burst may dominate the results
  • Using the JMeter GUI to run the actual load test — the GUI adds 20–40% overhead per thread and produces lower throughput numbers than a real run would achieve
  • Asserting the full response body as a string match ('response equals {"id":101,...}') — the exact JSON representation may change; assert key fields individually instead
  • Using the default Thread Group loop count of 1 instead of a time-based duration — a single loop ends in seconds and produces insufficient samples for percentile analysis
  • Forgetting the Constant Timer — without think time between requests, a 50-thread test generates far more load than 50 real users would, because real users pause to read pages
  • Not parameterising the base URL — hard-coding https://jsonplaceholder.typicode.com in every sampler makes the plan impossible to reuse against a staging environment without editing every request
  • Misreading P90 from the Aggregate Report — JMeter's 'Average' column is the mean, not a percentile; percentile columns are labelled '90%', '95%', '99%' and must be enabled in Aggregate Report settings

Submission checklist

  • Thread Group configuration documented: thread count, ramp-up, duration/loop
  • Three HTTP Request samplers documented with correct method, URL, and body where applicable
  • HTTP Header Manager with Content-Type: application/json on POST sampler
  • Response Assertions on all three samplers (status code + at least one body field)
  • Constant Timer or Gaussian Random Timer between requests
  • CSV Data Set Config documented with variable name, file path, sharing mode, and recycle settings
  • Sample postIds.csv with at least five rows
  • CLI run command with -n, -t, -l flags explained
  • Results section with P50, P90, P95, throughput, error rate, and pass/fail verdict
  • README notes covering base URL override and thread count parameterisation

Extension ideas

  • +Add a JMeter property for base URL (-Jbase_url) and update all samplers to use ${__P(base_url,https://jsonplaceholder.typicode.com)} so the plan can target any environment without .jmx edits
  • +Add a Throughput Controller to weight traffic across the three samplers (60% GET /users, 20% POST /posts, 20% GET /posts/{id}) to model realistic endpoint distribution
  • +Add a Response Time Graph listener and capture a screenshot of the throughput-over-time curve to identify stabilisation point after ramp-up