Performance & Security Testing
The non-functional half: what breaks under load, where the bottleneck actually is, and the security checks QA owns.
Fast Enough, Safe Enough
These are the tests most often skipped and most expensive to skip, because their failures arrive all at once, in production, at the worst possible moment — a launch, a sale, a news cycle.
Load Testing
The words are not interchangeable, and using the wrong one leads to testing the wrong thing.
| Test | Question |
|---|---|
| Load | Does it hold up at expected traffic |
| Stress | Where does it break, and how |
| Spike | What happens when traffic multiplies in seconds |
| Soak | Does it survive days — leaks, growing queues, full disks |
| Scalability | Does adding capacity actually help |
// k6: ramp up, hold, and fail the run on the percentile, not the mean
export const options = {
stages: [
{ duration: '2m', target: 200 }, // ramp
{ duration: '5m', target: 200 }, // hold
{ duration: '2m', target: 0 }, // ramp down
],
thresholds: {
http_req_duration: ['p(95)<500'],
http_req_failed: ['rate<0.01'],
},
};
A number without conditions is meaningless. "The API handles 2,000 requests per second" needs the hardware, the data volume, the mix of endpoints and the cache state, or it cannot be compared with anything — including itself next month.
Finding the Bottleneck
A load test that reports "it got slow" is only half the job. The useful output identifies the resource that ran out, and the shape of the graph usually says which.
| Symptom | Usual cause |
|---|---|
| Latency climbs while CPU stays low | Waiting — locks, a saturated pool, a slow dependency |
| Throughput plateaus, latency rises linearly | A queue: capacity reached, work backing up |
| Errors appear at a specific concurrency | A connection or thread pool limit |
| Slow degradation over hours | A leak — memory, file handles, unclosed connections |
| Database CPU pinned | Missing index, or N+1 queries under load |
Security Testing In QA
Deep security work belongs to specialists, but a QA team can catch a surprising share of real issues by testing the boundaries deliberately rather than only the happy path.
| Check | What you are looking for |
|---|---|
| Authorisation | Change an id in the URL — do you see someone else's data |
| Input handling | Script tags, quotes and huge payloads reflected or accepted |
| Rate limits | Can login be attempted a thousand times |
| Error messages | Stack traces, SQL and internal hostnames leaking to users |
| Session handling | Does logout end the session everywhere it should |
| Dependencies | Known-vulnerable packages, caught by a scanner in CI |
The most valuable of these is broken access control — changing an identifier to reach data belonging to another account. It is consistently the most common serious web vulnerability, it needs no tooling to find, and automated functional tests never look for it because every test signs in as the right person.
Interview Questions
Load, stress, spike or soak?
Load checks expected traffic, stress finds the breaking point, spike checks a sudden multiplication of traffic, and soak runs for days to expose leaks and slow degradation.
Why report percentiles rather than averages?
Averages hide the tail. A 200ms mean can contain 5% of users waiting four seconds — and those users are the ones who abandon. p95 and p99 describe real experience.
Latency rises but CPU stays low. What does that suggest?
Waiting rather than computing — lock contention, an exhausted connection pool, or a slow downstream dependency. Adding CPU will not help.
Why does test data volume matter?
Queries that are instant over a thousand seeded rows can be catastrophic over ten million. A small clean database hides the most common performance defect there is.
What security issue can QA reliably catch?
Broken access control — changing an id in a URL or payload to reach another account's data. It is the most common serious web flaw and normal functional tests never look for it.
What makes a performance number meaningful?
Its conditions: hardware, data volume, endpoint mix, cache state and concurrency. Without them the figure cannot be compared to anything, including a later run of the same test.