Performance Testing: Load, Stress & Soak Testing Explained
The Quick Answer
Performance testing measures how a system behaves under a given amount of work - how fast it responds, how much it can handle, and what happens when it runs out of capacity. It answers questions functional testing cannot: the feature works, but does it still work with ten thousand people using it at once?
The distinction that matters: Functional testing asks whether the system produces the right result. Performance testing asks whether it produces that result fast enough, for enough users, for long enough, without degrading.
The four types in one line each: Load testing checks behaviour at expected traffic. Stress testing pushes past it to find the breaking point. Soak testing holds a normal load for hours to find leaks and slow degradation. Spike testing applies a sudden surge to see whether the system absorbs or collapses.
This article covers what each type is for, which metrics to actually watch, how to set performance requirements before you test rather than after, how to run a test and interpret the results, and the mistakes that make performance results misleading. For where performance sits among the other categories of testing, see our overview of types of software testing.
What Performance Testing Measures
Performance is not a single quantity. A system can be fast for one user and unusable for a thousand, or fast for an hour and slow by the fourth. Four properties are worth separating.
- Speed - how long an individual operation takes, from the user's point of view rather than the server's
- Capacity - how much concurrent work the system can carry while still meeting its speed targets
- Stability - whether performance holds steady over hours and days, or drifts
- Scalability - how performance changes as load or resources change, which tells you whether adding capacity actually helps
Each of the test types below is designed to probe one or two of these. Running "a performance test" without deciding which property you are measuring is how teams end up with a pile of numbers and no conclusion.
Load, Stress, Soak and Spike Compared
| Type | Question It Answers | Load Profile | What You Learn |
|---|---|---|---|
| Load testing | Does the system meet its targets at expected traffic? | Ramp to expected peak, hold steady | Whether current capacity is adequate; baseline response times under realistic conditions |
| Stress testing | Where does it break, and how? | Increase past expected peak until failure | The actual capacity ceiling; which component fails first; whether failure is graceful or catastrophic |
| Soak testing | Does it stay healthy over time? | Moderate, realistic load held for hours or days | Memory leaks, connection pool exhaustion, log or disk growth, gradual response-time drift |
| Spike testing | Can it absorb a sudden surge? | Abrupt jump to several times normal load, then drop | Whether autoscaling reacts in time; whether the system recovers after the spike passes |
| Scalability testing | Does adding resources help? | Repeat the same load at increasing capacity | Whether scaling is roughly linear or hits a bottleneck that more instances cannot fix |
| Volume testing | Does it cope with large amounts of data? | Normal traffic against a database at realistic or future size | Query degradation, missing indexes, pagination and report failures at scale |
Two of these are consistently under-used. Soak testing catches the class of defect that is most expensive in production - the slow leak that takes eleven hours to manifest and therefore never appears in a twenty-minute test run. And volume testing catches the query that is instant against ten thousand rows and unusable against ten million, which is the most common performance surprise a growing product encounters.
Recovery Deserves Its Own Attention
Stress and spike tests are often stopped at the moment of failure, which discards half the information. What happens after the breaking point matters as much as where it is. A system that returns clear errors, sheds excess load, and recovers automatically when traffic drops is in a materially better position than one that needs a manual restart - even if both broke at the same number of users.
The Metrics That Matter
Performance testing produces a large amount of data, most of which is noise. A small set of metrics carries almost all the signal.
| Metric | What It Tells You | Watch Out For |
|---|---|---|
| Response time percentiles (p50, p95, p99) | What users actually experience, including the unlucky ones | Report percentiles, not averages - see below |
| Throughput (requests or transactions per second) | How much work the system is completing | Rising load with flat throughput means saturation, not capacity |
| Error rate | Whether the system is failing under load rather than just slowing | A "fast" result set achieved by returning errors quickly is not a pass |
| Concurrency | How many simultaneous users or connections are active | Must reflect realistic session behaviour, not just a request count |
| Resource saturation (CPU, memory, I/O, connections) | Which resource is the limiting factor | The first resource to run out is often not CPU; connection pools, file descriptors and I/O are easy to overlook |
| Latency distribution shape | Whether slowness is uniform or concentrated in a tail | A long tail usually points at queueing, garbage collection, or a single slow dependency |
The single most important habit: Stop reporting average response time. An average hides the tail, and the tail is where users suffer. A p50 of 200ms with a p99 of 9 seconds describes a system where one request in a hundred is unacceptable - and the average will read as comfortably fast. Set targets on p95 and p99, not on the mean.
Setting Requirements Before You Test
A performance test without a target produces a number, not a verdict. "The checkout completed in 2.4 seconds under 500 concurrent users" is not a pass or a fail until someone has said what acceptable looks like.
Performance requirements are non-functional requirements, and they need the same specificity as functional ones. A usable requirement names four things: the operation, the load condition, the metric and threshold, and the environment. For example - "search results return in under 1.5 seconds at p95 with 2,000 concurrent users on production-equivalent infrastructure." Each part is load-bearing: without the percentile the target is ambiguous, without the concurrency it is meaningless, and without the environment it is not reproducible.
Where the Numbers Come From
Do not invent thresholds. Derive them from production analytics where the system already exists - current peak concurrency, current p95 by endpoint, and the busiest hour of the busiest day give you a defensible starting point. Add headroom for growth, and be explicit about the multiple: testing at today's peak tells you nothing about the seasonal event that triples it. Where there is no production history, use business expectations - expected users, expected transactions per hour - and revisit once real traffic exists.
These requirements belong somewhere durable rather than in a test plan that is written once. Recording them as expected results attached to the relevant test cases in test case management means each performance run is measured against a stated threshold rather than against whatever the last run happened to produce.
How to Run a Performance Test
The sequence matters, because most invalid performance results come from skipping a step rather than from a bad tool.
- Define the scenario. Pick the operations that matter - usually the highest-volume and highest-value journeys - and the realistic mix between them. A test that hammers one endpoint tells you about that endpoint, not about the system.
- Prepare the environment. Use infrastructure that resembles production in shape, not just in software version. Half the instances, a smaller database, or a shared runner will produce numbers you cannot extrapolate.
- Prepare the data. The database should be at realistic size, and virtual users need distinct accounts and inputs. This is where performance tests most often go wrong; see our guide to test data management.
- Establish a baseline. Measure at low load first. Without a baseline you cannot tell whether a slow result is caused by load or was always slow.
- Ramp, do not jump. Increase load gradually except when spike testing, and let the system stabilise at each level before recording.
- Monitor the system, not just the client. Client-side timings tell you there is a problem; server and infrastructure metrics tell you where it is.
- Run long enough. Short runs miss garbage collection cycles, cache warming effects, and anything cumulative. Discard the first minutes as warm-up.
- Change one thing at a time. Tuning several parameters between runs makes the result uninterpretable.
Realistic Behaviour, Not Just Realistic Numbers
Virtual users that fire requests back to back with no pause generate load no real population produces. Include think time between actions, model session length, and mix new and returning users - because a cold cache and a warm one behave very differently. The goal is a load profile that resembles your traffic, not the maximum number of requests a script can emit.
Reading the Results
Interpretation is where performance testing delivers or wastes its value. A few patterns cover most findings.
Throughput plateaus while load rises. The system is saturated. Response times will climb from this point regardless of how many more users you add, because work is queueing. The number where the plateau starts is your real capacity.
Response time climbs gradually from the start. Usually a resource contention issue - a connection pool, a lock, a single-threaded component - rather than raw capacity. Look for the resource that reaches saturation first.
A sharp cliff rather than a curve. Something has a hard limit: a pool exhausted, a timeout cascade, a dependency rate-limiting you. Cliffs are usually configuration, not hardware.
Errors rise before response times do. Something is rejecting work rather than queueing it. Check circuit breakers, rate limits, and upstream timeouts.
Slow drift over hours with stable load. The signature of a leak - memory, connections, file handles, or unbounded log growth. This is exactly what soak testing exists to find.
In all cases, resist reporting a single headline number. The useful output of a performance test is a statement of capacity with conditions attached: this operation meets this threshold up to this concurrency on this configuration, and beyond it this component becomes the limit. That is actionable; "the system handles 5,000 users" is not.
Performance Testing in CI/CD
Full-scale performance runs are too slow and too expensive to attach to every merge, but leaving performance testing until the week before release means finding problems at the worst possible moment. The workable compromise is a tiered approach.
Run a short performance smoke test on every build - a handful of critical operations at modest load, taking a few minutes, checking response times against a threshold. It will not find capacity limits, but it reliably catches the change that made a key endpoint five times slower, on the day it was introduced.
Run a full load test on a schedule - nightly or weekly - against a production-like environment, with results trended over time rather than judged individually. The trend is the valuable artefact: a p95 that has crept up 30% over six weeks is a finding no single run would produce.
Reserve stress, soak, and spike testing for release candidates and for changes that alter the architecture, add a dependency, or are expected to change traffic significantly. Treat threshold breaches in the per-build check as build failures rather than warnings, or they will be ignored within a month. How this fits the wider pipeline is covered in our guide to continuous testing in DevOps, and keeping trends visible across runs is what reporting is for.
Common Mistakes
- Testing on undersized infrastructure. Results from an environment a quarter the size of production cannot be scaled up by multiplying - bottlenecks do not move proportionally.
- Reporting averages. The average hides the tail that users actually complain about.
- Testing without a stated threshold. Producing numbers with no pass criterion turns a test into a measurement exercise.
- Unrealistic data volume. A fast query against an empty database proves nothing about the same query at production scale.
- Ignoring the client and network. Server-side response time is not user-perceived performance; payload size, round trips, and rendering matter.
- No think time. Zero-delay virtual users create a load pattern that does not exist and often saturate a component real traffic never would.
- Stopping at the breaking point. How the system fails and whether it recovers is at least as important as the number at which it fails.
- Running once. A single performance test is a snapshot. Without repetition there is no trend, and without a trend regressions are invisible until they are severe.
Conclusion
Performance testing is a family of related activities rather than a single check. Load testing verifies expected traffic, stress testing finds the ceiling and the failure mode, soak testing exposes leaks and drift, spike testing tests absorption and recovery, and volume testing catches what happens as data grows. Each answers a different question, and running one is not a substitute for the others.
What separates a useful performance effort from a wasted one is rarely the tool. It is having explicit thresholds set before the run, an environment and dataset that resemble production, percentiles instead of averages, and results tracked over time so that a gradual regression is visible while it is still cheap to fix.
That last part is the piece teams most often lack. Trend the raw numbers in your load-testing and observability tooling, where the percentiles live. What is usually missing is the record around them: which threshold applied, which build was judged against it, and whether it passed. QA Sphere holds the threshold as the expected result on the test case, captures the verdict and a link to the tool's report on every test run, and shows the pass and fail history across runs and releases in reporting - so a threshold that has started failing is visible next to the rest of your quality picture rather than buried in a dashboard nobody opens. See pricing or book a demo.
Written by
QA Sphere TeamThe QA Sphere team shares insights on software testing, quality assurance best practices, and test management strategies drawn from years of industry experience.



