Testing & QA

Game Development » Testing & QA

Games are among the hardest software to test: they are large, stateful, real-time, non-deterministic, and judged on feel as much as correctness. A regression in a spreadsheet app is a wrong number; a regression in a game can be a soft-lock that traps a player forever, a physics edge case that launches the avatar into orbit, or a memory leak that crashes the console after six hours — a class of bug a unit test almost never catches. This page covers the full quality stack: human playtesting for fun and usability, automated testing (unit, integration, and replay/determinism) for correctness, performance and soak testing for stability under load and over time, bug triage to turn a flood of reports into a prioritized work queue, console certification (TRC/TCR/lotcheck), and telemetry to close the loop with live data from real players.

Table of contents

  1. Testing & QA
    1. Why Game Testing Is Different
    2. Playtesting
      1. Kinds of Playtesting
      2. Running a Usability Test
      3. Quantitative Playtest Metrics
    3. Automated Testing
      1. Unit Tests
      2. Integration (“Play Mode”) Tests
      3. Replay and Determinism Tests
      4. Other Automated Techniques
    4. Performance and Soak Testing
      1. Performance Testing
      2. Soak (Stability/Endurance) Testing
      3. Stress and Load Testing
    5. Bug Triage
      1. A Good Bug Report
      2. Severity vs. Priority
      3. The Triage Workflow
    6. Certification (Console TRC/TCR)
    7. Telemetry
      1. What to Collect
      2. The Telemetry Pipeline
      3. Closing the Loop
    8. Putting It Together: A QA Pipeline
    9. See Also

Why Game Testing Is Different

Traditional software QA assumes you can pin down inputs and assert on outputs. Games violate every part of that assumption:

Property Consequence for testing
Real-time Bugs are timing-dependent; a frame hitch can change behavior. You must test at frame rate, not at your own pace.
Stateful The bug may only appear after 40 hours of specific play. State space is effectively infinite.
Non-deterministic Floating-point, threading, and input timing make “repro steps” unreliable. Reproducibility is itself an engineering goal.
Subjective “Is it fun?” and “does this feel responsive?” cannot be asserted in code. Humans are required.
Content-heavy Thousands of assets, levels, and quests, each a potential failure point. Coverage is a content problem, not just a code problem.
Platform-fragmented The same build must pass on PC, multiple consoles, and a range of GPUs. A bug may exist on exactly one SKU.

The practical takeaway: no single technique is sufficient. Effective game QA is a layered defense — cheap automated checks catch the many shallow bugs continuously, while expensive human playtesting and soak runs catch the few deep ones. The layers, ordered from fastest/cheapest to slowest/most-expensive:

        Telemetry (live, real players)        <- millions of sessions
      ┌─────────────────────────────────┐
      │   Certification / Cert passes    │     <- days, per submission
      │   Soak & performance testing     │     <- hours, nightly
      │   Manual / structured playtesting│     <- humans, per build
      │   Replay / determinism tests     │     <- minutes, per commit
      │   Integration / smoke tests      │     <- minutes, per commit
      │   Unit tests                     │     <- seconds, per commit
      └─────────────────────────────────┘

Each layer should catch what the layer above it would have caught more expensively. A determinism bug found by a replay test in CI costs minutes; the same bug found in cert costs a failed submission and a slipped ship date.

Playtesting

Playtesting is the irreplaceable human layer. It answers questions automation cannot: Is the tutorial clear? Is the difficulty curve fair? Is the core loop fun? Did the player even find the door? There are several distinct kinds, often confused.

Kinds of Playtesting

Type Question answered Who tests When
Usability / UX Can players understand the controls and UI without being told? Fresh, representative players Early and continuously
Fun / design Is the core loop engaging? Is pacing right? Target-audience players Throughout production
Balance Are difficulty, economy, and power curves tuned? Skilled and novice players Mid-to-late
Functional / QA Does it work? Find bugs and break things. Professional testers Continuously
Compliance / cert Does it meet platform requirements? Specialist testers Pre-submission
Compatibility Does it run across the hardware matrix? Test lab with device farm Late

A common mistake is using developers or friends for usability testing. People who built the game (or know the builder) cannot un-know how it works; they will subconsciously avoid the dead end a first-time player walks straight into.

Running a Usability Test

The gold-standard technique is think-aloud, no-help observation:

  1. Recruit representative players — match your target audience, and critically, people who have never seen the game.
  2. Set a task, not a script — “Get to the first save point,” not “Press X to open the door.”
  3. Say nothing. The single hardest and most valuable rule. When a tester is stuck, the designer’s instinct is to help. Resist it — that stuck moment is the data. If you have to explain it in the room, you have to explain it in the shipped game, and you cannot sit next to every player.
  4. Ask them to think aloud — narrate what they believe, expect, and are confused by.
  5. Record screen + face + audio. Watch for the gap between what players say afterward and what they did in the moment; behavior is more reliable than self-report.
  6. Look for patterns across testers, not single opinions. One person disliking the jump is noise; five people failing the same jump is a design bug.

Quantitative Playtest Metrics

Even “soft” playtesting can be instrumented. Useful per-session metrics:

  • Time-to-first-action and time-to-completion per objective (spikes reveal confusion).
  • Failure/retry counts per encounter (difficulty spikes).
  • Drop-off point — where players quit (the most important retention signal).
  • Path heatmaps — where players actually go vs. where you intended.

These feed directly into the telemetry systems used post-launch; the same instrumentation built for playtests scales to millions of live players.

Automated Testing

Automated tests are the cheap, continuous layer that runs on every commit and frees humans to do work only humans can do. Three families matter for games: unit, integration, and replay/determinism.

Unit Tests

Unit tests target pure, deterministic logic in isolation: damage formulas, inventory math, save serialization, pathfinding cost functions, state-machine transitions. They are fast, hermetic, and the backbone of CI. The key to making game code unit-testable is separating logic from the engine — extract the rules into plain classes with no dependency on rendering, time, or input.

// Pure logic, no MonoBehaviour, no engine — fully unit-testable.
public static class DamageCalculator
{
    public static int Resolve(int baseDamage, int armor, bool isCrit)
    {
        int mitigated = Mathf.Max(1, baseDamage - armor);   // never below 1
        return isCrit ? mitigated * 2 : mitigated;
    }
}

[Test]
public void Armor_Reduces_Damage_But_Never_Below_One()
{
    Assert.AreEqual(1, DamageCalculator.Resolve(baseDamage: 5, armor: 100, isCrit: false));
    Assert.AreEqual(7, DamageCalculator.Resolve(baseDamage: 10, armor: 3, isCrit: false));
    Assert.AreEqual(14, DamageCalculator.Resolve(baseDamage: 10, armor: 3, isCrit: true));
}

Most engines ship a unit-test framework: Unity Test Framework (NUnit-based, “Edit Mode” tests run without entering play), Unreal Automation (IMPLEMENT_SIMPLE_AUTOMATION_TEST), and GUT / GdUnit for Godot.

Integration (“Play Mode”) Tests

Integration tests spin up part of the real game — a scene, the physics world, a few systems wired together — and assert on emergent behavior over several frames. In Unity these are “Play Mode” tests that can yield across frames:

[UnityTest]
public IEnumerator Player_Lands_On_Ground_Within_Two_Seconds()
{
    var scene = SceneManager.LoadScene("TestArena", LoadSceneMode.Single);
    yield return null;                       // let one frame run
    var player = Object.FindObjectOfType<PlayerController>();
    player.transform.position = new Vector3(0, 10, 0);

    float t = 0;
    while (!player.IsGrounded && t < 2f)
    {
        t += Time.deltaTime;
        yield return null;                   // advance the real game loop
    }
    Assert.IsTrue(player.IsGrounded, "Player never landed — gravity or collision broken.");
}

Smoke tests are the most valuable integration tests: load every level, spawn the player, run for N frames, assert “no exceptions, no NaN transforms, no missing-reference errors.” This catches the catastrophic, content-driven breakages (a level that crashes on load because an artist deleted a prefab) that no unit test can. A nightly job that boots every map is one of the highest-ROI tests a studio can own.

Replay and Determinism Tests

The most powerful game-specific automated test records a session as a stream of inputs plus an initial seed, then replays those inputs and asserts the simulation reproduces an identical end state (often via a checksum of game state per frame). This requires — and enforces — a deterministic simulation.

Record:   seed + [frame 0: jump] [frame 12: shoot] [frame 30: move-left] ...
Replay:   feed identical inputs into a fresh sim with the same seed
Assert:   state_checksum(frame N) == recorded_checksum(frame N)   for all N

Replay tests deliver three big wins:

  1. Regression detection. Any change that alters simulation output — even a one-tick physics difference — fails immediately, with the exact frame of divergence.
  2. Free repros. Record a tester’s crash, and the input log is a perfectly reproducible repro the moment determinism holds.
  3. Lockstep multiplayer validation. Deterministic lockstep netcode (RTS, fighting games) is literally a replay system across machines; replay tests are the same machinery and catch desyncs.

Determinism is hard to retrofit. The usual culprits, all of which a checksum-diffing replay test will surface:

  • Floating-point differences across compilers/architectures — use fixed-point or carefully constrained float math for the authoritative sim.
  • Iteration order of hash maps and unordered sets — sort or use ordered containers in sim code.
  • Uninitialized memory and per-machine RNG seeding.
  • Frame-rate-coupled logic — the sim must advance on a fixed timestep, decoupled from render rate (see Game Loop Architecture).

Other Automated Techniques

  • AI/monkey testing — bots that wander, mash inputs, and try to escape the level mesh. Excellent at finding holes in collision and “out of bounds” exploits no human would think to try.
  • Snapshot/golden-image testing — render a known scene and diff the framebuffer against an approved reference (with a tolerance) to catch unintended graphics regressions.
  • Property-based testing — generate thousands of random valid inputs to a system (e.g., random inventories) and assert invariants (“total item count is conserved”) rather than specific cases.

Performance and Soak Testing

Functional correctness is necessary but not sufficient: a game that is correct but stutters, or correct but crashes after four hours, fails. Two related disciplines cover this.

Performance Testing

The goal is to verify the game holds its frame budget under worst-case load and to catch performance regressions before they compound. A 60 FPS target is a 16.67 ms frame budget; 30 FPS is 33.3 ms. The headline number is the budget, but the metric that matters is the tail, not the average.

Metric Why it matters
Average frame time Coarse health check; hides stutter.
95th/99th-percentile frame time One bad frame in a hundred is still visible as a hitch. Tails are what players feel.
1% and 0.1% lows (FPS) Standard reporting for the worst frames; directly correlates to perceived smoothness.
Frame-time variance / hitch count Consistency beats peak; a steady 45 FPS often feels better than 60 FPS that drops to 20.

Build automated performance tests that run a fixed camera path or scripted scenario in CI and fail the build if frame time, draw calls, or memory exceed a threshold. Capturing this per-commit turns a slow “the game got worse over the last month” into “commit a1b2c3 added 4 ms — revert it.”

Profiling tools are platform-specific and essential: in-engine profilers (Unity Profiler, Unreal Insights/stat unit), GPU tools (RenderDoc, PIX, Nsight), and platform SDK profilers on console. For the full discipline of finding and fixing the bottlenecks these tests reveal, see the Performance Optimization section, in particular GPU and CPU optimization.

Soak (Stability/Endurance) Testing

A soak test leaves the game running — often for days — to surface bugs that only manifest over time and are nearly impossible to find by hand. Soak testing is frequently a hard certification requirement (e.g., “must survive N hours idle at the title screen and N hours in gameplay without crashing”).

What soak tests catch that nothing else does:

  • Memory leaks — a few KB leaked per spawn is invisible in a 5-minute test but exhausts a console’s fixed RAM after hours and crashes.
  • Memory fragmentation — even without a true leak, allocation churn fragments the heap until a large allocation fails. Brutal on consoles with no virtual-memory safety net.
  • Accumulator overflow / precision drift — a float game-time counter loses precision after hours; an int frame counter can overflow. Physics far from the origin degrades (the classic “far lands” floating-point jitter).
  • Resource handle leaks — file handles, audio voices, GPU resources slowly exhausted.
  • Heat/throttle behavior on mobile and console (covered in Platform-Specific Tuning).

Practical soak setups: an idle soak (sit at a menu or pause screen for 24-72 h), a gameplay soak (an AI bot or replay loop playing continuously), and a fast-forward soak (run the sim at high speed to simulate long sessions quickly, when the engine supports decoupled timestep). Always monitor a memory graph over the whole run — a steadily rising line with no plateau is a leak, full stop.

Stress and Load Testing

For multiplayer, load testing simulates many concurrent clients (often hundreds of headless bot clients) against the server to find the player-count cliff, validate matchmaking, and size the backend before a launch spike melts it. This is the netcode analogue of soak testing and pairs with the architecture concerns in Networking and Multiplayer.

Bug Triage

Testing produces bugs; triage turns an unmanageable pile into an ordered work queue. Without triage a team drowns: a thousand open bugs with no priority is functionally the same as no bug database at all.

A Good Bug Report

The single biggest lever on QA throughput is report quality. A complete report contains:

  • Repro steps — numbered, minimal, deterministic where possible.
  • Expected vs. actual behavior.
  • Build/version, platform, and hardware SKU.
  • Evidence — screenshot, video, and crucially the input replay log (if the engine supports it) and a crash dump / call stack.
  • Frequency — always, intermittent, or once.

“Sometimes the game crashes” is nearly worthless; “100% repro: on build 4471/PS5, mash dodge during the boss intro cutscene — call stack attached” is actionable immediately.

Severity vs. Priority

These are independent axes and conflating them is a classic triage error:

  • Severity = impact if it happens (objective: cosmetic / minor / major / crash / data-loss / cert-blocker).
  • Priority = order to fix (a decision: blocker / high / medium / low), a function of severity and frequency and cost-to-fix and proximity to ship.

A rare crash in an unreachable debug menu is high severity but low priority. A common, ugly UI flicker on the main menu is low severity but high priority because every player sees it on launch. Triage is the meeting where the team assigns priority by weighing all of these.

A simple priority heuristic:

                 High frequency        Low frequency
High severity    P0 — fix now          P1 — fix this sprint
Low severity     P2 — fix when near    P3 — backlog / won't-fix

The Triage Workflow

A typical bug lifecycle and the states a tracker (Jira, our version-control workflows, etc.) models:

New → Triaged(priority+owner assigned) → In Progress → Fixed
    → Verified(QA confirms on a new build) → Closed
                              │
                              └→ Reopened (regression / not actually fixed)

Key triage practices: hold a regular triage (often daily near ship), maintain a “cert blocker” lane that supersedes everything before submission, and track fix-then-verify discipline — a bug is not closed when a developer says “fixed,” it is closed when QA verifies the fix on a fresh build. The “Verified” step exists precisely because “works on my machine” fixes routinely fail to reproduce on the actual target hardware.

Certification (Console TRC/TCR)

To ship on a console (or some closed platforms), the build must pass the platform holder’s certification — a formal test pass against a published requirements checklist. Naming varies by platform but the concept is identical:

Platform holder Term
Sony (PlayStation) TRC — Technical Requirements Checklist
Microsoft (Xbox) XR / TCR — Xbox Requirements / Technical Certification Requirements
Nintendo (Switch) Lotcheck (guideline-based check)
Valve (Steam Deck) Verified program (a lighter compatibility review, not gated cert)

Certification is not a fun, balance, or quality judgment — the platform holder does not care whether your game is good. It is a strict, often pedantic compliance pass on platform consistency and user-experience standards. Typical requirement categories:

  • Terminology — correct, exact names for hardware and UI elements (e.g., the controller button or account terminology spelled and capitalized exactly as the platform mandates).
  • Suspend/resume — the game must correctly handle the console sleeping and waking mid-session.
  • Controller disconnect — pause and present the right prompt when a controller’s battery dies or it disconnects.
  • Account/profile changes, sign-out, and multi-user handling.
  • Storage — graceful handling of a full or removed storage device mid-save; save-corruption resilience.
  • Network loss — correct messaging and recovery when connectivity drops.
  • Stability — must pass extended soak with no crashes (the soak testing above exists largely to pass this).
  • Performance and rating floors, age-rating display, and trophy/achievement correctness.

Strategic realities of cert:

  • Certification can fail, and a failed submission means a fix, a re-submission, and lost days or weeks — directly threatening a fixed launch date and any coordinated marketing.
  • Treat the TRC/TCR as a test suite from day one. Many requirements (suspend/resume, controller disconnect) are architectural — bolting them on at the end is far more expensive than designing for them.
  • Many studios maintain an internal “pre-cert” pass that runs the platform checklist before official submission, so the expensive external cert is a formality, not a gamble.

Telemetry

Pre-launch testing samples a few hundred players at most; telemetry instruments the game to report data from every live session, turning your entire player base into a continuous, massive QA and design feedback loop. It is how modern (especially live-service) games are tuned and stabilized after release.

What to Collect

  • Stability — crash reports with call stacks and minidumps (the highest-value telemetry; tools like Sentry/Backtrace/Crashlytics aggregate them).
  • Performance — frame-time distributions and hardware specs sampled in the wild, revealing the long tail of GPUs you could never test in the lab.
  • Progression — where players quit (drop-off / funnel), completion rates, time-per-level.
  • Balance/economy — weapon pick/win rates, currency sources and sinks, difficulty spikes (encounters with abnormal death rates).
  • Behavior — heatmaps of where players go, die, and get stuck.

The Telemetry Pipeline

Game client → batch events → ingest endpoint → event store / warehouse
            → dashboards & alerts → design + engineering decisions
                                  → balance patch / hotfix → back to client

Practical engineering considerations:

  • Batch and send asynchronously off the main thread; telemetry must never cost frame time or block gameplay.
  • Sample high-frequency events (you rarely need every player’s every position at 60 Hz — aggregate or sample).
  • Version every event so you can interpret data across patches.
  • Respect privacy and law — disclose collection, honor opt-outs, and comply with regulations like GDPR/CCPA. Telemetry handling is a real compliance obligation, related to the broader concerns in Cybersecurity.

Closing the Loop

The point of telemetry is action. A drop-off cliff at level 3 sends designers to fix pacing; a weapon with a 70% win rate triggers a balance patch; a crash spike on one GPU driver triggers a hotfix. This is the same instrumentation discipline begun in quantitative playtesting — scaled from a focus group of ten to a live audience of millions, and feeding directly back into the next build.

Putting It Together: A QA Pipeline

A mature studio runs these layers continuously rather than as a final phase:

Per commit (CI):   unit + smoke + replay/determinism + perf-threshold tests
Nightly:           full smoke across all levels + automated perf run + short soak
Per build:         structured playtest + QA functional pass + bug triage
Weekly/milestone:  balance playtest + compatibility/device-farm pass
Pre-submission:    internal pre-cert pass + extended soak + load test
Live:              telemetry, crash aggregation, hotfix loop

The throughline of this entire page: testing is not a phase at the end, it is a layered, continuous system. Shift detection as far left (and as automated) as you can afford — every bug caught by a unit test in seconds is a bug that did not cost a playtester an afternoon, a failed cert submission a week, or a launch-day refund a customer.

See Also