Unreal Engine: Testing and Profiling

After last week’s state machines, this week was about testing and profiling: the [Automation Test Framework]https://dev.epicgames.com/documentation/unreal-engine/automation-test-framework-in-unreal-engine, functional and unit testing, and performance analysis with Unreal Insights – the discipline that keeps everything built so far from quietly rotting.
Notes below.
Automated Testing
The starting questions: what can go wrong, and what do you test to build your test cases?
The motivating example from the course: a third-person stealth game whose core feature is AI perception. You want to guarantee AI sight always works. But with a large world and many levels containing spawners, testing a change by playing through the entire game is tedious – so you set up tests to make sure the major feature isn’t broken.
Error handling vs testing
An important distinction – especially between silent error handling and asserts/breaks:
- Asserts cause game-breaking crashes: execution does not progress beyond the assert. They’re needed to ensure required pointers or objects are valid – preventing a whole class of bugs caused by a missing pointer or object
- But asserts (and
Ensure) only catch what they’re told to catch. They won’t catch logic errors – only testing catches those- Example: an enemy killing a friendly because the logic never checked whether the target is an ally. No pointer is null; nothing asserts. A test map with two allies, run for a number of seconds, asserting that neither ally took damage, catches it
Functional Testing
The AI perception demo as a functional test:
- Create a folder for test maps and assets
- Create a map for the test
- Create a Blueprint of type Functional Test
- Fill in the logic and call the Finish Test node - without it, the test times out and reports failure
- Create variables and functions to keep the test’s logic legible
Keeping tests alive: functional tests must not go stale. When code or logic changes, the test gets updated or deleted. Stale tests become like overwhelming warning messages – they get ignored, and eventually the whole test suite gets ignored because nobody can tell whether a test failed or simply stopped being relevant.
Unit Testing
Examples of what unit tests are good for:
- Leaderboards: unit testing that the scoring logic ranks correctly
- Stress-testing a spatial algorithm: generate random coordinates, find the closest point, and brute-force the expected answer to verify the sort
- A quadtree implementation: feed it thousands of locations to verify the getters return what’s intended
- Small utilities: string lengths, floating-point precision
Profiling
Performance is a key factor in build stability: as the game grows in complexity, performance drops. Unreal Insights tracks framerate and shows why.
Context for the numbers: a frame processes game logic, rendering, and various engine/game code. If the logic takes too long to process, the game slows down. The ideal framerate is 60; some games ship at 30; VR targets 90+.
Workflow with the test project (a Scan feature on the player doing a sphere trace, tied to an input action):
- Start a trace - the button in the lower right starts it; press again to stop
- Parse the information looking for spikes - There’s always a spike when the game loads in: BeginPlay and Construction Script events. Expected, not a bottleneck
- Click on a frame to see the breakdown of functions called and their timing
- Filter for Sphere Cast and double-click the function to see the time taken
- Remove other charts to reduce noise; zoom out to see the sphere cast in isolation
- Click anywhere on the timeline to show that frame’s breakdown; clicking PerformScan shows the detailed breakdown of functions called
What this bought us: a direct pointer at the bottleneck. Vector math calls turn out to be very fast – the slow part was elsewhere.
Quick console alternative: stat Unit brings up the per-frame timing stats overlay.
Notes on things that stood out
Two things this week reframed how I think about the whole course’s worth of work. First, the assert/testing distinction is really a humility lesson: asserts catch the bugs you imagined, tests catch the bugs you didn’t. The friendly-fire example is perfect – nothing crashes, nothing logs, the game is just wrong.
Second, the stale-test warning is sneakier than it sounds. A test suite with a few dead tests trains you to ignore red, which is functionally identical to having no test suite – with all the maintenance cost and none of the benefit. Tests are code, and code rots.
And stat Unit is going to be muscle memory from here on out.
Related Posts
2025-10-22
2025-10-15
2025-10-08