The ISTQB Performance Testing exam (CT-PT) asks 40 questions in 90 minutes, and 20 of them come from one chapter of the syllabus: Performance Testing Tasks. You pass with 26 points out of 40. So the best use of your study time is to work that chapter once with your own hands: write an operational profile, turn it into a load profile, do the throughput arithmetic, and put the result in a script.
This post does that with one small example. The scripts use k6 because it is free and reads like plain JavaScript. The syllabus names no tool at all, so treat the code as a way to make the ideas concrete, not as exam content.
What does the CT-PT exam look like?
CT-PT is a 40-question multiple-choice exam in ISTQB's Specialist stream, and you need the Foundation Level certificate before you can sit it. The figures below are from the ISTQB page for Certified Tester Performance Testing, read on 14 October 2026.
| Item | Value |
|---|---|
| Questions | 40 |
| Points | 40, one per question |
| Passing score | 26 points (65%) |
| Time | 90 minutes, or 113 if the exam is not in your native language |
| Prerequisite | Certified Tester Foundation Level (CTFL) |
| Syllabus | v1.0, released in 2018 |
| Fee | Not listed by ISTQB. The exam provider you book with sets it |
Where do the 40 questions come from?
Half of them come from chapter 4. ISTQB publishes the split per chapter in its Exam Structures and Rules tables (v1.19, August 2026), and the syllabus gives each chapter a minimum training time.
| Chapter | Questions | Training time |
|---|---|---|
| 1. Basic Concepts | 6 | 60 min |
| 2. Performance Measurement Fundamentals | 4 | 55 min |
| 3. Performance Testing in the Software Lifecycle | 7 | 195 min |
| 4. Performance Testing Tasks | 20 | 475 min |
| 5. Tools | 3 | 90 min |
The same tables sort the questions by thinking level. Two are K1 (remember), 25 are K2 (understand), 4 are K3 (apply) and 9 are K4 (analyze). Ten of the 13 apply and analyze questions sit in chapter 4.
There is a timing catch in those tables. They allow 1 minute for a K1 question, 2 for K2, 3 for K3 and 4 for K4. For CT-PT that adds up to 100 minutes of nominal work (2 + 50 + 12 + 36) in a 90-minute exam. The only place to win the ten minutes back is the 25 K2 questions, so the definitions have to come fast.
How do you turn usage data into an operational profile?
An operational profile is one distinct pattern of interaction with the application, and you build it in three steps: decide what data you need, gather it, and evaluate it. The syllabus asks for three kinds of data: the user personas and their roles, the generic tasks each role performs, and the estimated number of users for each role per unit of time.
It also tells you where to look. Sources include workshops with product owners and sales managers, functional specifications and user stories, usage logs from the current system, and watching real users work through tasks. The approach is top-down. Start with broad profiles and split one further only if a test objective needs it.
Here is the example used in the rest of this post. It is a made-up ticket shop, not something from the syllabus.
| Operational profile | One session | Think time | Volume at peak |
|---|---|---|---|
| Visitor | Open the home page, list events, open one event | 9 s after each step | 100 sessions open at any moment |
| Buyer | Log in, search, add to cart, pay | 9 s after each step | 19,800 orders an hour |
Two profiles are enough. The learning objective for this section is K4, analyze, so be ready to read usage data like this and say which profile matters. The syllabus singles out profiles whose tasks run often, need critical or frequent transactions between components, or move large volumes of data.
How do you build a load profile from the operational profiles?
A load profile says how many instances run each operational profile, and how that number changes over the test. When the instances stand for people, they are called virtual users. The syllabus lists four shapes to combine: ramp-ups, ramp-downs, steps, and predefined distributions that copy a daily or seasonal cycle.
Section 4.2.4 of the CT-PT syllabus builds a stress profile from two parts. A step of 100 virtual users runs the first operational profile for the whole test as background load. A second group ramps up to 220 virtual users, holds for two hours, and ramps down. Added together, they give the system three hours of stress.
In k6 that diagram is two scenarios:
export const options = {
scenarios: {
background: {
executor: 'constant-vus', // a step: all 100 start at once
exec: 'visit',
vus: 100,
duration: '3h',
},
stress: {
executor: 'ramping-vus',
exec: 'buy',
startVUs: 0,
stages: [
{ duration: '30m', target: 220 }, // ramp-up
{ duration: '2h', target: 220 }, // hold
{ duration: '30m', target: 0 }, // ramp-down
],
},
},
};
The syllabus does not print the ramp lengths. Thirty minutes each is my choice, so that the two ramps and the two-hour hold fill the three hours.
How many virtual users give the throughput you planned?
Divide the number of virtual users by the time one pass takes, and you have the throughput. The syllabus writes it like this:
system throughput = virtual users / (processing time + think time)
It then warns that a user count alone is not a measure of load. Its example: 500 users who each run a short query every minute produce 30,000 queries an hour. The same 500 users running one query an hour produce 500. The user count is the same and the load differs by a factor of 60.
Now the ticket shop. A buyer session has four transactions. Say each takes 1 second to process and is followed by 9 seconds of think time. That is 4 seconds of processing and 36 seconds of thinking, 40 seconds a pass.
const targetPerSecond = 19800 / 3600; // 5.5 orders a second
const pass = 4 + 36; // seconds for one session
const users = targetPerSecond * pass; // 220 virtual users
// The system slows down under stress: 4 s of processing becomes 14 s
const slowPass = 14 + 36; // 50 seconds
const actual = users / slowPass; // 4.4 orders a second
console.log(users, Math.round(actual * 3600)); // 220 15840
The same 220 virtual users now place 15,840 orders an hour, 20% under the plan. This is the sentence in the syllabus that candidates skip: when processing time goes up, throughput can drop even if everything else stays the same. Your stress test eases off at the moment it should push hardest.
The syllabus explains why. A typical test setup is a closed system: the number of users is fixed, so when all of them are waiting for a response, no new user can arrive. Many public systems are open systems, where new users keep arriving while the current ones wait. The k6 documentation describes the same split in its page on open and closed models. To hold the arrival rate steady, swap the executor:
stress_open: {
executor: 'constant-arrival-rate',
exec: 'buy',
rate: 11,
timeUnit: '2s', // 11 sessions every 2 seconds = 5.5 a second
duration: '2h',
preAllocatedVUs: 300, // 5.5 x 40 s = 220 in flight, 5.5 x 50 s = 275
},
What does the syllabus expect inside a performance test script?
It expects three parts: an initialization section, a main section that can run many times, and a clean-up section. k6 uses different names for the same idea. Its test lifecycle has init code and setup() for initialization, a scenario function for the main part, and teardown() for clean-up.
import http from 'k6/http';
import { check, group, sleep } from 'k6';
import { SharedArray } from 'k6/data';
import { Trend } from 'k6/metrics';
import { scenario } from 'k6/execution';
// 1. Initialization: load test data and declare metrics. No requests here.
const buyers = new SharedArray('buyers', () => JSON.parse(open('./buyers.json')));
const orderTime = new Trend('order_time', true);
const BASE = 'https://shop.test.example';
const JSON_HEADERS = { 'Content-Type': 'application/json' };
export function setup() {
// Still initialization. A broken system gives no useful load numbers.
const res = http.get(`${BASE}/api/health`);
if (res.status !== 200) throw new Error('system under test is not ready');
}
// 2. Main section: one pass is one buyer session.
export function buy() {
const me = buyers[scenario.iterationInTest % buyers.length]; // parameterization
let spent = 0;
group('order', () => { // outer transaction
let res = http.post(`${BASE}/api/login`, JSON.stringify(me), { headers: JSON_HEADERS });
check(res, { 'logged in': (r) => r.status === 200 });
const token = res.json('token'); // correlation
const headers = { ...JSON_HEADERS, Authorization: `Bearer ${token}` };
spent += res.timings.duration;
sleep(9); // think time
res = http.get(`${BASE}/api/events?city=${me.city}`, { headers });
const eventId = res.json()[0].id; // correlation again
spent += res.timings.duration;
sleep(9);
group('add to cart', () => { // nested transaction
res = http.post(`${BASE}/api/cart`, JSON.stringify({ eventId, seats: 2 }), { headers });
check(res, { 'two seats in cart': (r) => r.json('seats') === 2 });
spent += res.timings.duration;
});
sleep(9);
group('pay', () => {
res = http.post(`${BASE}/api/orders`, null, { headers });
check(res, { 'order confirmed': (r) => r.json('status') === 'CONFIRMED' });
spent += res.timings.duration;
});
});
orderTime.add(spent); // response time of the whole order, think time left out
sleep(9);
}
// 3. Clean-up
export function teardown() {
http.post(`${BASE}/api/test-data/reset`);
}
The visit function for the background scenario is left out to keep this short. Five things in the script map to lines of the syllabus:
-
Transactions. A transaction runs from the start of a request to the end of the processing it triggers. Transactions can be nested, so one run measures each step and the order as a whole. That is what the inner groups and the
order_timemetric do. -
Think time. The syllabus states that transaction response time plus think time equals elapsed time. The
sleep(9)calls are the think time, and the trend records response time only. - Timers. Time a meaningful unit of work, such as submitting an order. A protocol-level script like this one measures server and network time only. A script that drives the GUI measures end to end.
-
Result verification. Checking the HTTP return code is the minimum. The
checkcalls look at what came back: two seats, a confirmed order. - Scripts are software. Review the script and test it with the whole range of input data before trusting its numbers.
Correlation gets its own paragraph in the syllabus. If you record a session at the protocol level, the recording holds identifiers that were valid once, such as the login token above. They have to become variables that are filled from each response. The syllabus says incorrect or missing correlation is the main reason recorded scripts fail on playback, and the k6 docs carry examples of extracting tokens from responses for JSON and for form fields.
Parameterization is the other half. Run every virtual user with the same name and the same data, and the results mislead in one of two directions. The data may be fully cached, which makes the system look faster than production. Or users may queue on a lock for the same record, which makes it look slower.
That is why the script reads buyers from a file and gives each iteration a different one. The k6 page on data parameterization shows the same pattern with JSON and CSV files.
Why does the report use a percentile and not only an average?
Because the average can be skewed by outliers, and the syllabus says so in those words. It lists four ways to report transaction response time: minimum, maximum, average and a percentile such as the 90th. Ten response times show the problem:
const ms = [180, 190, 200, 210, 220, 230, 240, 250, 2600, 4100];
const avg = ms.reduce((a, b) => a + b, 0) / ms.length;
const pct = (q) => [...ms].sort((a, b) => a - b)[Math.ceil(q * ms.length) - 1];
console.log(avg, pct(0.5), pct(0.9)); // 842 220 2600
Eight of the ten users waited a quarter of a second or less. Two waited seconds. The average of 842 ms describes none of them. The median shows the typical user, and the 90th percentile shows the tail.
The syllabus adds two points that are easy to miss. A goal of 100% compliance is not recommended, because the resources it needs may be too large for a minor gain to users. And you look at the status of the virtual users before anything else. A failed transaction takes far less time than a completed one, so failures can make the transactions-per-second figure look better than it is.
In k6 you write the goal down as a threshold, and the run fails when it is missed. The thresholds reference lists the aggregation methods you can use.
export const options = {
thresholds: {
http_req_failed: ['rate<0.01'], // under 1% failed requests
order_time: ['p(90)<3000'], // 90% of orders within 3 s of response time
},
};
Which test type fits which objective?
The syllabus defines seven types under the umbrella term performance testing, and chapter 1 carries six questions. The first two columns below follow section 1.2. The third column is my shorthand for the load profile each one usually needs.
| Type | What it focuses on | Typical load shape |
|---|---|---|
| Load | Handling increasing levels of anticipated, realistic load | Ramp up to the expected level and hold |
| Stress | Peak loads at or beyond the specified limits, or reduced resources | Ramp past the expected maximum |
| Scalability | Meeting future needs, such as more users or more stored data | Repeated runs with growing load or data |
| Spike | Sudden bursts of peak load, then a return to a steady state | Step up, step down |
| Endurance | Stability over a period that fits the system's use | Flat load for a long time |
| Concurrency | Specific actions that happen at the same moment | Many users doing one thing at once |
| Capacity | How many users or transactions it supports while meeting its objectives | Step up until an objective fails |
Endurance testing is the one that finds resource problems which build up slowly. The syllabus gives memory leaks, database connections and thread pools as its examples.
A short video on the exam
ProcessExam's channel has a three-minute video with five preparation tips for CT-PT. It covers the study side that this post leaves out.
How should you split your study time?
Follow the syllabus's own weighting. The five chapters add up to 875 minutes of minimum training time, about 14 and a half hours, and chapter 4 takes 475 of them. A plan that gives chapter 4 half of your hours matches both the training time and the question count.
A split that works in practice:
- Chapters 1 and 2 first, in one sitting. They are ten questions, mostly definitions, and they are where you win back time on exam day.
- Chapter 4 over three or four sittings, with a keyboard. Build one operational profile, one load profile and one script for a system you know.
- Chapter 3 next. Its seven questions are about risk: which performance risks belong to which architecture, and which test activity belongs to which lifecycle phase.
- Chapter 5 last. Three questions on tool types and how to judge whether a tool suits a project.
If you want every learning objective in one place to tick off, the CT-PT syllabus topics listed by chapter on ProcessExam work as a checklist.
Finish with ISTQB's own sample exam, which is a free download on the certification page. Time yourself at 90 minutes. The answer sheet gives the learning objective and K-level of every question, so note which level each wrong answer was.
For the weeks before the exam, this CT-PT preparation playbook goes deeper into planning and revision than a code walkthrough can.
Frequently asked questions
1. How many questions are on the ISTQB CT-PT exam, and what is the passing score?
The exam has 40 multiple-choice questions worth one point each, and you need 26 points to pass, which is 65%. You get 90 minutes, or 113 minutes if the exam is not in your native language.
2. Do I need the Foundation Level certificate before taking CT-PT?
Yes. ISTQB states that candidates must hold the Certified Tester Foundation Level certificate to gain the Performance Testing certification.
3. Does the CT-PT exam test a specific tool such as JMeter or k6?
No. The syllabus names no tool. Chapter 5 covers types of tools (load generators, load management consoles, monitoring tools) and how to judge whether a tool suits a project, and it carries 3 of the 40 questions.
4. Which chapter of the CT-PT syllabus has the most exam questions?
Chapter 4, Performance Testing Tasks, has 20 of the 40 questions. Chapter 3 has 7, chapter 1 has 6, chapter 2 has 4 and chapter 5 has 3.
5. Is the 2018 CT-PT syllabus still the current version?
Yes, as of 14 October 2026. The ISTQB certification page offers CT-PT Syllabus v1.0 (2018) as its syllabus download, and the Exam Structures and Rules tables dated August 2026 still list CT-PT with 40 questions and 90 minutes.
Written by the team behind ProcessExam, a practice-test site for process, project and software testing certifications. We are not affiliated with ISTQB.

Top comments (0)