Soak and Endurance Testing
Soak testing, also called endurance testing, is a type of performance testing that holds a realistic, steady load on a system for a long time — many hours, sometimes days — to find problems that only appear with time. The ISTQB performance testing syllabus uses the name endurance testing and ties the duration to the system's operational context — in practice, how long it really runs between restarts, deployments or maintenance windows. It also names what such a test looks for: resource capacity problems, such as memory leaks or exhausted database connections and thread pools, that slowly degrade performance or end in failures.
Example: an order API passes a 30-minute load test at 180 requests per second with a 95th-percentile (p95) response time of 310 ms. The same load held for 12 hours tells another story: memory grows by about 0.24 GB every hour, p95 climbs above 1.4 seconds, and after about 11.5 hours the container is killed for exceeding its 4 GB memory limit. The load never changed; only time passed.
Why it matters
Short tests measure a system that has just started: pools are clean, logs are small and every leak is still tiny. In production the same service may run for days or weeks between deployments. A defect that leaks a few megabytes per hour, or keeps one database connection after each timed-out payment, is invisible in a 30-minute run and becomes an outage on day four — often "fixed" by a restart that hides the cause until it happens again.
For the product, a soak test is the evidence that performance and reliability hold for the real operating period, not only for the first minutes; in ISO/IEC 25010 terms it examines resource utilisation and time behaviour over time. For the tester it changes the question from "is it fast?" to "does it stay fast, and does it stay up?" — and a leak found in a test environment, with full monitoring and no customers affected, is far easier to diagnose than the same leak in production.
What makes a soak test different
In the previous lessons each test type changed the load: a load test applies the expected workload, a stress test goes past the expected peak, and a spike test makes the load jump suddenly. A soak test keeps the load steady and makes the duration the variable. The result you look for is therefore a trend: does anything keep rising or falling while the load stays flat?
Soak testing and endurance testing name the same activity. ISTQB uses endurance testing; many tools and teams, including the k6 documentation, say soak testing. Some teams draw their own line between the two, for example by load level, but there is no standard split, so state the meaning in your test plan.
- Load: usually the expected average production load, or a realistic busy-period load, held constant. Stay clearly below the breaking point found in stress testing, or you cannot tell overload from a slow leak.
- Duration: taken from the operational context — at least one full business cycle, such as a day with its nightly batch jobs, or the longest time the service runs between restarts or deployments.
- Question: does the system behave in hour 12 as it did in hour 1, and does it give its resources back when the load stops?
What degrades over time: leaks, exhaustion and drift
A memory leak happens when a program keeps holding memory it no longer needs, so that memory is never released. Many runtimes (Java, .NET, JavaScript, Go) free memory automatically: the garbage collector (GC) regularly removes from the heap — the memory area where a program keeps its objects — every object that nothing refers to any more. In these runtimes a leak therefore means the program still holds references to objects it no longer uses: an in-memory cache without a size limit, a session list that is never cleaned, a listener added on every request and never removed. Memory rises and falls in a sawtooth as the collector runs. The signal is the level after each collection: in a healthy service it settles after warm-up; with a leak it keeps climbing until the process runs out of memory or the platform kills it. Before that, the collector works harder and response times grow.
Resource exhaustion is the same pattern with a resource that has a fixed limit. A code path that does not return its database connection to the pool when an error occurs leaks one connection each time it runs. When the pool is empty, new requests wait and then time out, although CPU and memory look normal.
- Memory: process or heap memory after garbage collection rising hour after hour.
- Pools and handles: database connections, threads, open files or sockets in use creeping towards their configured maximum.
- Disk: logs, temporary files or uploads filling the disk; without log rotation the service stops when the disk is full.
- Queues: a consumer slightly slower than its producer, so the backlog and the waiting time grow without end.
- Degradation without a leak: tables that grow during the run slow down unindexed queries, and scheduled jobs such as backups compete with traffic at a fixed hour. These are real findings too, but the fix differs, so name them precisely.
- Healthy service: memoryMemory after garbage collection rises while the service warms up, then stays level far below the limit.
Reference line:memory limit (4 GB)
- 1warm-up 0–1 h
- 2steady state 1–12 h
- Memory leak (worked example)Heap after garbage collection grows about 0.24 GB an hour, from 1.2 GB at hour 1 to 3.6 GB at hour 11, and never levels off.
Reference line:memory limit (4 GB)
container killed at 11:35: out of memory
- Connection leakDatabase connections in use climb a few at a time; when the pool is empty, new requests wait and time out.
Reference line:pool size
pool exhausted: requests time out
Schematic: 12 hours of constant load, each resource shown as a share of its limit. The memory-leak plot follows the worked example; the other two are illustrative.
Planning and running a soak test
A soak test without server-side monitoring only tells you that the system got slower. Collect, at a fixed interval and on the same timeline as the response times, the four golden signals from Google's SRE book — latency, traffic, errors and saturation — plus every resource that can leak: memory after garbage collection, connections and threads in use, open files, free disk and queue depth.
Write the pass criteria before the run, and compare time windows instead of averaging. A 12-hour average hides a trend; compare the first steady hour with the last one, and check short windows for errors.
- Use the realistic workload mix and enough distinct test data: 20 accounts reused for 12 hours keep everything in cache and hide growth, and unique values such as order numbers must not run out mid-run.
- Keep the environment production-like — the same memory limits, log level and scheduled jobs — and agree that nobody deploys or restarts anything during the run.
- Watch the load generator too: if it runs short of memory or ports, the system under test looks slow when it is not.
- After the ramp-down, keep observing: memory, connections and threads should return close to their starting level.
Soak test plan: order API
Workload: 180 requests/s (expected weekday average), held constant
60% browse, 25% search, 10% add to cart, 5% checkout
5,000 distinct test users
Duration: 15 min ramp-up, 12 h steady state, 10 min ramp-down,
then 30 min observation with no load
Monitor: every minute: p95 and p99 per endpoint, throughput, errors,
heap after GC, DB connections in use (pool max 50),
threads, open files, free disk, queue depth
Pass if: 1. p95 in the last hour <= 1.2 x p95 in the first steady hour
2. error rate < 0.5% in every 15-minute window
3. heap after GC: no upward trend over the last 6 hours
4. no restarts and no out-of-memory kills
5. after ramp-down: heap and connections back near baselineTry it: Performance Test Data Generator Try it: Load Test Data Generator Try it: Concurrent Request Test Generator
Worked example: reading a 12-hour run
The order API from the plan ran for 12 hours at a steady 180 requests per second. The table below shows selected hours that the tester exported from the monitoring dashboard.
Read it column by column. Throughput is flat, so the slowdown is not caused by more traffic. Connections stay between 12 and 14 of 50, so the pool is not the problem. Heap after garbage collection (GC) rises by about 0.24 GB per hour and never levels off: a memory leak. p95 grows with it because the collector runs longer and more often as the heap fills, and errors appear in the last hours. The run fails criteria 1 to 4; criterion 5 could not be checked because the run never reached ramp-down.
The defect report states facts and the trend, not a guess at the cause: the build, load profile, duration, the heap chart, the time of the kill, and the projection that at average load production would reach the same limit roughly every 11 to 12 hours. Ask the developers to capture two heap dumps in the test environment, in hour 1 and hour 6; comparing them shows which objects keep growing. After the fix, repeat the same profile as confirmation testing and compare the curves.
Hour Req/s p95 ms Errors Heap after GC DB conns in use
1 180 310 0.1% 1.2 GB 12 of 50
3 180 325 0.1% 1.7 GB 13 of 50
6 180 410 0.2% 2.4 GB 12 of 50
9 180 690 0.4% 3.1 GB 14 of 50
11 180 1450 1.9% 3.6 GB 13 of 50
11:35 container killed: memory limit (4 GB) exceededCommon mistakes
- Calling a long load test a soak test without monitoring resources: you see that the system slowed down, but not why.
- Running above the breaking point found in stress testing, so overload and slow leaks mix and neither can be diagnosed.
- Judging the run by its overall average instead of comparing the first and last hours and checking short windows.
- Choosing the duration by convenience ("one hour before lunch") instead of from the real operating period.
- Reusing a handful of test users or records, so caches hide growth — or running out of unique data mid-run and failing for the wrong reason.
- Accepting a scheduled restart or a bigger memory limit as the fix: it only moves the failure further out in time.
The QA perspective
A soak test costs calendar time and environment time, so plan it early. Agree with the product owner and operations how long the service must run unattended, reserve a production-like environment overnight or over a weekend, and make sure nobody deploys to it during the run. Ask developers and operations which resources have hard limits — memory limits, pool sizes, disk quotas, queue sizes — so you know what to monitor before the run starts, not after it fails.
Record the build, the workload profile, the duration and the pass criteria with the results, and keep the charts: the strongest evidence in a soak report is a resource that climbs under flat load. Many teams run a shorter soak for each release candidate and a long one before major releases or infrastructure changes. Compare each run with the previous one: memory that settles 20% higher than last month, or a p95 that drifts more within the run, is worth raising even when every criterion still passes.
Practice exercise
Diagnose an 8-hour soak run
A payment service ran a soak test at a constant 60 requests per second for 8 hours. Its pass criteria: p95 at most 1.2 times the first-hour p95, error rate below 0.5% in every hour, and no upward trend in any monitored resource. The monitoring export is below.
Decide whether the run passed, identify what degraded and why you think so, estimate when the problem would hit production at this load, and list the evidence you would put in the defect report.
Payment service soak test: 60 requests/s, 8 hours, DB pool max 40
Hour Req/s p95 ms Errors Heap after GC DB conns in use Threads
1 60 220 0.0% 610 MB 8 of 40 52
2 60 225 0.0% 615 MB 12 of 40 52
4 60 230 0.0% 612 MB 20 of 40 53
6 60 235 0.1% 618 MB 29 of 40 52
7 60 610 1.8% 615 MB 34 of 40 53
8 60 5000 9.6% 614 MB 40 of 40 52
Most frequent error from hour 7: "Timeout waiting for a connection from the pool"
Bank simulator timeouts (planned in the test data): 4 to 6 per hour
Request timeout configured in the service: 5 s- Check whether the load stayed constant.
- Look at each resource column and decide which one trends and which ones are flat.
- Relate the most frequent error to the trending resource.
- Compare the results with each pass criterion and decide pass or fail.
- Estimate the time to failure and list the evidence and the questions for the developers.
Show the model answer
The load stayed at 60 requests per second, so the change is caused by time, not traffic. Heap after GC (610–618 MB) and threads (52–53) are flat: no memory or thread leak. DB connections in use climb from 8 to 40 of 40, about 4 to 6 per hour, until the pool is exhausted.
The errors confirm it: from hour 7 the most frequent error is a timeout while waiting for a pool connection, and p95 jumps from 235 ms to 610 ms and then to 5,000 ms, which is the configured request timeout. The run fails: p95 in hours 7 and 8 is above 1.2 × 220 = 264 ms, the error rate is above 0.5% in hours 7 and 8, and connections trend upwards from the first hour. Had the run stopped at hour 6, it would still have failed on the trend criterion while response times and errors looked fine — which is why a soak test monitors resources.
Time to failure: at this load, a fresh start runs normally for about 6 hours; from about hour 7 users get slow responses and errors, and at about hour 8 the pool is empty (40 connections, 4 to 6 leaked per hour). In production it can be sooner: the leak rate matches the 4 to 6 bank timeouts per hour, so a day with more bank timeouts would empty the pool faster. Treat that link as a hypothesis for the developers, not a proven cause.
Evidence for the defect report: build and environment, load profile and duration, the connections-in-use chart next to the flat heap and threads, the first time p95 and errors broke the criteria, the error message with counts, and the correlation with bank timeouts. Ask the developers whether the timeout path returns its connection, and whether the pool in use has a leak-detection setting that logs where a connection was taken — some pools offer one. After the fix, rerun the same 8-hour profile and confirm that the connection count stays flat.
Frequently asked questions
How long should a soak test run?
As long as the operating period you need confidence in: at least one full business cycle including scheduled jobs, or the longest time the service runs between restarts or deployments. If a resource grows slowly, run long enough to see a clear trend and estimate when it would reach its limit. Systems that run for weeks may need days of testing.
Is a soak test just a long load test?
The load profile can look the same, but the goal and the evidence differ. A load test checks that targets are met at the expected load; a soak test checks that behaviour stays stable over time, so it needs resource monitoring and a comparison of early and late windows, not only response-time targets.
If memory keeps growing, is it always a leak?
No. Memory can grow legitimately while caches fill, as long as it levels off at a configured limit. Look at the level after garbage collection over many hours: a curve that settles is warm-up; a curve that keeps climbing under constant load points to a leak. Report the trend and let the developers confirm the cause with heap analysis.
Can I run a soak test with Mutqan tools?
Not the run itself: a soak test needs a load tool such as Apache JMeter, k6, Gatling or Locust running for hours against an environment you are allowed to load. Mutqan tools help with the preparation: request mixes and ramp profiles, large sets of distinct test data, and a starting script for a load tool.
References
- Certified Tester Performance Testing (CT-PT)ISTQB's specialist syllabus on performance testing: basic concepts, performance measurements, test activities and tasks, and tools.
- Load test types — Grafana k6 documentationDefinitions of the common load test types — smoke, average-load, stress, soak, spike and breakpoint — and what each one is meant to reveal.
- Monitoring Distributed Systems — Site Reliability Engineering (Rob Ewaschuk)Google's SRE guidance on monitoring: black-box versus white-box monitoring and the four golden signals — latency, traffic, errors and saturation.
- ISO/IEC 25010:2023 — Systems and software Quality Requirements and Evaluation (SQuaRE) — Product quality modelThe product quality model: functional suitability, performance efficiency, compatibility, interaction capability, reliability, security, maintainability and flexibility.
Practise with Mutqan tools
Generate performance test inputs with realistic distributions: think times, request mixes, payload sizes and ramp profiles.
Generate large parameter datasets (users, tokens, ids, payloads) for k6, JMeter, Gatling and Locust load tests.
Generate concurrency test scripts (k6, JMeter JMX, Locust, Artillery, Node.js) with safe defaults for a target endpoint.
Go deeper
Your progress on this topic
Not started
Mark the topic complete when you have studied it and done the exercise.
Sign in to track your progress
Mark topics complete, continue where you left off and unlock specialisation exams.