Resource library

QA How-To

Performance Testing Basics for QA

Learn practical performance testing: workload models, latency percentiles, load scripts, bottleneck analysis, baselines, pass criteria, and clear reports.

1,936 words | Article schema | FAQ schema | Breadcrumb schema

Overview

Performance testing is not a contest to generate the largest request count. It is an experiment that asks whether a system meets user expectations under a stated workload, and why behavior changes as demand grows. A result such as 800 requests per second is meaningless without response-time percentiles, errors, resource signals, data conditions, and a model of who generated those requests.

This guide gives QA engineers a practical path from business usage to a trustworthy test. You will define measurable objectives, model traffic, script realistic journeys, prepare an environment, observe the entire stack, locate bottlenecks, and communicate results without hiding tail latency behind averages. The approach works with tools such as k6, JMeter, Gatling, Locust, or a service-specific harness. Tool choice follows the experiment, not the reverse.

Choose the Performance Question First

Different tests answer different questions. A load test checks expected and peak demand. A stress test increases demand until service degrades and reveals the limiting resource. A spike test applies a sudden jump, useful for ticket launches or notification-driven traffic. An endurance or soak test holds load for hours to reveal leaks and gradual exhaustion. A volume test challenges large datasets, while a scalability test compares behavior as capacity is added.

Write the question in operational language. For example: Can checkout sustain 120 completed orders per minute for thirty minutes while p95 confirmation time remains under two seconds and the error rate stays below 0.5 percent? This statement identifies the journey, workload, duration, percentile, and failure budget. It prevents a technically impressive run that cannot support a release decision or capacity plan.

  • Load tests validate expected service levels.
  • Stress tests find the breaking point and recovery behavior.
  • Spike tests examine rapid elasticity and queue protection.
  • Soak tests expose leaks, accumulation, and resource churn over time.

Use Metrics That Describe User Experience

Latency measures how long an operation takes. Throughput measures completed work per unit of time. Concurrency describes overlapping users or operations. Error rate is the proportion that fails, but define failure carefully: timeouts, invalid responses, and business rejections may need separate categories. Saturation describes how close a constrained resource is to its limit. These measures must be read together because high throughput is not success if users wait ten seconds or receive errors.

Use percentiles instead of relying on the average. If nine requests finish in 100 milliseconds and one takes 9 seconds, the average hides a painful tail. p95 means 95 percent completed at or below that value. Report p50 for typical behavior and p95 or p99 for the tail, plus maximum only as diagnostic context. Match service-level objectives to the user journey, not an arbitrary tool default.

  • Response time: p50, p95, and p99 by operation
  • Work rate: requests, transactions, or business outcomes per second
  • Reliability: error and timeout rate by cause
  • Capacity: CPU, memory, pools, queues, connections, and downstream limits

Build a Workload Model From Real Usage

A workload model describes which actions occur, how often, in what sequence, with what data, and for how long. Use production analytics, access logs, forecasts, campaign plans, and business targets. Separate read-heavy browsing from write-heavy checkout or upload activity. Account for daily peaks, regional overlap, background jobs, and cache warmness. Converting monthly active users directly into simultaneous virtual users usually creates a fictional load.

For an event platform, the mix might be 65 percent event searches, 20 percent detail views, 10 percent seat holds, and 5 percent purchases. Users pause between actions, abandon flows, and reuse sessions, so include realistic pacing and correlation. Model arrival rate when the business cares about incoming work per second, and closed virtual users when session concurrency is the closer representation. Document assumptions and produce low, expected, peak, and stress profiles.

  • Derive transaction mix from evidence, then review it with product and operations.
  • Include think time, session reuse, abandonment, and background traffic.
  • Model hot keys and uneven data access, not only uniform random IDs.
  • Keep expected-load and limit-finding scenarios separate.

Write Scripts That Validate Correct Work

A load script must prove that responses are correct enough to count. Checking only HTTP 200 can record an error page or empty result as success. Assert status, critical fields, business outcome, and timing. Correlate dynamic values such as authentication tokens, cart IDs, and anti-forgery tokens instead of replaying captured constants. Parameterize accounts and records so virtual users do not overwrite the same entity or benefit from an unrealistic single cache key.

A compact k6-style check might be `const res = http.get(`${baseUrl}/catalog/${itemId}`); check(res, { 'status 200': r => r.status === 200, 'has price': r => r.json('price') > 0 });`. Add tags such as operation and region so results can be separated. Keep script logic lightweight, because excessive client-side parsing can make the load generator the bottleneck. Version scripts and test data with the application.

Prepare an Honest Test Environment

A useful environment matches production in the components that determine the performance question: topology, instance class, autoscaling rules, database engine and indexes, cache settings, network path, third-party limits, and representative data volume. Exact duplication may be too expensive, but every difference should be listed with its expected effect. Running against a tiny empty database on one shared QA server cannot predict production capacity.

Control competing activity and confirm monitoring before the run. Seed realistic distributions, warm caches only if production will be warm, and include a cold-cache scenario when startup matters. Synchronize clocks across injectors and services. Validate that load generators have spare CPU, memory, network, and file descriptors at target load. Notify stakeholders, define stop conditions, and never direct an unapproved stress test at production or a paid dependency.

  • Record application build, configuration, infrastructure, and dataset version.
  • Run a low-load validation before committing to the full test window.
  • Confirm dashboards, traces, and logs cover every critical dependency.
  • Set safety limits for errors, saturation, and cost.

Run in Phases and Preserve Comparability

Start with a smoke load to validate scripts and data. Then ramp gradually so you can observe the first point where latency or errors change. Hold the target plateau long enough for caches, pools, autoscaling, and garbage collection to stabilize. Ramp down and watch recovery, because a system that stays impaired after traffic falls is operationally dangerous. Repeat the run when natural variability could change the conclusion.

Change one major factor at a time when investigating. If code, database size, instance count, and test mix all change, you cannot attribute the result. Store configuration and raw result files with a run identifier. Compare releases using the same environment and workload, but periodically update the model when real user behavior changes. A stable baseline is a controlled reference, not a frozen view of last year's traffic.

Find the Bottleneck With Correlated Evidence

When response time rises, align client metrics with server metrics on one timeline. Check whether CPU, garbage collection, memory, disk latency, database locks, connection pools, thread pools, queues, or downstream calls change at the same point. Distributed traces can show where transaction time accumulates, while database plans expose scans or lock waits. A saturated resource is a clue, but correlation alone does not prove cause.

Form a hypothesis and run a controlled comparison. If database connection wait rises before p95 latency, increase the pool only within safe database capacity or reduce connection holding time, then repeat the same load. If results improve, the evidence supports the hypothesis. Avoid moving the constraint blindly, such as enlarging an application pool until the database collapses. The useful output of performance testing is an explained limit and an engineering action, not a screenshot of a red graph.

  • Align generator, service, database, queue, and dependency clocks.
  • Find the first metric that bends, not only the loudest later failure.
  • Test one remediation hypothesis against the same workload.
  • Verify recovery and watch for the next limiting resource.

Set Pass Criteria and Regression Budgets

Define pass criteria before seeing the result. Include workload, duration, percentile latency, error rate, business throughput, and resource or recovery constraints. A criterion might read: at 300 catalog requests per second for twenty minutes, p95 stays below 600 milliseconds, unexpected errors remain under 0.2 percent, database CPU stays below 75 percent, and queues drain within two minutes after ramp-down. All conditions must hold together.

For continuous performance checks, use a smaller repeatable scenario and compare against a baseline with an allowed regression budget. Account for normal variation by using repeated samples or a meaningful threshold, not a one-millisecond difference. Block changes only when the environment is stable enough to make the signal trustworthy. Larger capacity and soak tests can remain scheduled or release-focused while trend results are still visible to developers.

Report Decisions, Not a Wall of Charts

A performance report should state the question, environment, build, data, workload, duration, pass criteria, results, bottleneck evidence, limitations, and recommended action. Lead with the decision: meets expected peak, fails the target, or remains inconclusive. Show a few aligned charts for throughput, latency percentiles, errors, and the constrained resource. Break down failures by operation because one endpoint can disappear inside aggregate numbers.

Make uncertainty explicit. If the environment had half the production database memory or excluded a slow third party, explain what can and cannot be inferred. Attach scripts, configuration, and raw output for reproduction. Track capacity over time in business units such as completed checkouts per minute per instance. This helps product and engineering decide whether to optimize, scale, change a dependency, or adjust a release plan.

  • State the release or capacity decision in the opening paragraph.
  • Compare actual results with predeclared criteria and prior baselines.
  • Name the evidence-backed constraint and proposed next experiment.
  • List environmental gaps and their likely direction of bias.

Frequently Asked Questions

What is performance testing in QA?

Performance testing measures how a system behaves under a defined workload. It evaluates user response time, completed work, errors, resource use, stability, scalability, and recovery so the team can make capacity and release decisions.

What is the difference between load testing and stress testing?

Load testing checks whether the system meets targets at expected or peak demand. Stress testing increases demand beyond that level to find the breaking point, observe failure behavior, and confirm the system recovers when load falls.

How many virtual users should a performance test use?

Derive the number from real concurrency, arrival rates, transaction mix, and business forecasts. A virtual-user number without pacing and workflow context is not portable, so model operations per second or concurrent sessions according to how the product is actually used.

Why is p95 response time better than average response time?

An average can hide a slow minority of requests. p95 shows the threshold at or below which 95 percent of operations finish, making tail delays visible. Use p50, p95, p99, errors, and throughput together for context.

Can performance testing be done in CI?

Yes, with a short, stable regression scenario and controlled environment. Use it to detect meaningful changes against a baseline, while larger peak, stress, and soak tests usually run on a schedule or before significant releases.

How do you identify a performance bottleneck?

Align load-generator results with service, database, queue, and dependency metrics, then find what changes first as latency or errors rise. Form a cause hypothesis, change one relevant factor, and repeat the same workload to confirm or reject it.

Related QAJobFit Guides