Automated Testing in UE: Stop Breaking What You Weren't Touching

Created: 2026-07-25

With ten features, every fix means checking ten things. How to build a UE Functional Test (an Actor you place in a test level that runs and judges itself), write assertions, and run it from Session Frontend — with a hands-on that tests HP going 100 to 80 and deliberately turns it red first.

You fixed the projectile collision. It works now. Then later you notice the door won't open — a place you never touched has broken.

In a game with ten features, every fix means checking ten things by hand before you can relax. Twenty features, twenty checks. Automated testing hands that repetition to the machine, and it ships with UE. This article covers Functional Tests — writable in Blueprint alone — and takes you through building one, running it, and deliberately turning it red before making it green.

A soft blue clay figure watching a checklist turn green on its own

What You'll Learn

  • Where checking by hand breaks down
  • UE has two kinds of tests (Blueprint users start with Functional Test)
  • Making a Blueprint with Functional Test as its parent
  • Writing assertions with the Assert nodes
  • Running them from Session Frontend
  • Hands-on: test HP going 100 → 80, and turn it red once

Sponsored

Where Checking by Hand Breaks Down

On a small prototype, checking by hand is faster. Three features, three things to poke.

The problem is that feature count and checking effort multiply.

As features grow, the number of places to re-check after each fix grows with them
FeaturesThings to check after one fixReality
33Manageable by hand
1010You start skipping some
2020You stop checking everything

And what you skipped is what breaks. The place you were sure you hadn't touched was affected through a shared variable or a common parent class.

The value of automated testing is that confirming nothing is broken becomes free, every time. Write it once, run it forever.

This series has run "break it on purpose, then fix it" many times. Automated testing is the flip side: the tool for confirming, every time, that it's still not broken.


Two Kinds of Tests

UE has two kinds of tests, and which one you start with changes the difficulty enormously.

Functional Tests place an Actor in a level that runs and judges, while C++ tests verify at function level
KindWhat it doesSuitsRequires
Functional TestPlaces an Actor in a test level and runs it for real, then judgesGameplay verification (the shot hits, the door opens)Blueprint only
C++ testsVerifies at function levelCalculation logic (damage formulas)C++

If you build in Blueprint, Functional Tests are the right entry point.

Functional Tests have a distinctly UE-shaped design: the test itself is an Actor you place in a level.

  • Make a test level
  • Place a test Actor in it (a Blueprint parented to Functional Test)
  • That Actor drives the game itself and judges the result

It's less "writing test code" and more "building a test stage." It's nearly the same work as making a game, so it's approachable if you're comfortable in Blueprint.


Your First Functional Test

Three steps.

Making a Blueprint parented to Functional Test, placing it in a test level, and writing from Start Test to Finish Test
  1. Make a test level (L_Test_Damage). It's test-only, so a floor and a light is enough
  2. Make a Blueprint with Functional Test as its parent class (search under "All Classes")
  3. Place that Blueprint in the test level

Parenting to Functional Test unlocks dedicated events.

Event / NodeRole
Event Start TestThe test begins. Write your verification from here
Finish TestThe test ends. Hand it success or failure
Assert familyCompares against expected values (next section)

The crucial part is always calling Finish Test. Without it the test never ends and fails by timeout.

The value you pass to Finish Test's Test Result decides the outcome.

  • Succeeded — passed
  • Failed — failed
  • Invalid — preconditions weren't met, so it wasn't a valid test

Writing Assertions

The verification itself uses the Assert nodes — nodes that check whether the actual value matches what you expected.

NodeChecks
Assert TrueThe condition is true
Assert FalseThe condition is false
Assert Equal (Int)An integer matches the expected value
Assert Equal (Float)A float matches (with a tolerance)
Assert Is ValidA reference is valid

Every one of them has a Message pin. Whatever you write there appears in the log when it fails.

Assert Equal compares expected against actual, and on mismatch the Message text lands in the log

Always write the Message. Knowing "a test failed" without knowing what differed means investigating from scratch. Write something like "HP after damage doesn't match the expected value" — a sentence that tells you where to look the moment you read it (→ Print String and the Output Log).

Careful with float comparisons. Assert Equal (Float) takes a Tolerance. Floating-point math produces tiny discrepancies, so around 0.0001 of slack is normal. Integer has no such error, so compare it directly.

Sponsored

Running Them

Tests run from Session Frontend.

Selecting tests on Session Frontend's Automation tab and hitting run produces a results list
  1. Open Tools → Session Frontend
  2. Select the Automation tab
  3. Find your test in the tree and check it. Functional Tests appear under Project
  4. Press Start Tests

The test level loads automatically, the test runs, and results appear in a list. Green passed, red failed.

Open a failed row and you see the text you wrote in the Assert's Message. That tells you where to go.

If your test doesn't appear in the list, you forgot to place the Actor in the test level. Functional Tests are discovered by being placed in a level, so creating the Blueprint alone isn't enough.


Hands-On: Test That HP Goes 100 → 80

Roguelike damage math, fighting-game combo scaling, RPG elemental multipliers. Whether numbers are correct is exactly what playing the game won't reveal. We'll turn the damage handling verification into a test, and deliberately turn it red first.

What you need

ItemDetails
BP_EnemyVariables MaxHealth (Float, 100.0) and CurrentHealth (Float, 100.0). Event AnyDamage subtracts Damage from CurrentHealth
Test levelL_Test_Damage (floor and light only)
Test ActorBP_Test_Damage (parent: Functional Test)

Decide the values the test uses up front.

VariableTypeValue
TestDamageFloat20.0
ExpectedHealthFloat80.0 (100 − 20)
The test failing red, then turning green once the cause is fixed

What goes in the test

Four steps on BP_Test_Damage's Event Start Test.

  1. Spawn Actor from Class to place BP_Enemy in the test level (the origin is fine)
  2. Call Apply Damage (Damaged Actor: the spawned enemy, Base Damage: TestDamage = 20.0)
  3. Assert Equal (Float) comparing the enemy's CurrentHealth against ExpectedHealth (80.0), with Tolerance 0.01 and Message "HP after damage doesn't match the expected value"
  4. Call Finish Test (Test Result = Succeeded)
A two-row completed node graph: the top row "Spawn an enemy and hit it" runs Event Start Test into Spawn Actor from Class and Apply Damage, wrapping into the bottom row "Compare and finish" with Get CurrentHealth, Assert Equal (Float) and Finish Test
BP_Test_Damage (Event Graph)
Event Start Test
  → enemy = SpawnActorFromClass(Class = BP_Enemy, Transform = origin)
  → ApplyDamage(
        DamagedActor = enemy,
        BaseDamage   = TestDamage)          // 20.0
  → AssertEqual_Float(
        Actual    = enemy.CurrentHealth,
        Expected  = ExpectedHealth,         // 80.0
        Tolerance = 0.01,
        Message   = "HP after damage doesn't match the expected value")
  → FinishTest(TestResult = Succeeded, Message = "")

A failing Assert marks the test as failed on its own, so passing Succeeded to Finish Test is fine (if the assertions pass, it simply succeeds).

First, turn it red

This is the important part: proving the test actually works.

Deliberately break BP_Enemy's Event AnyDamage subtraction. Change CurrentHealth - Damage to CurrentHealth - 0, or remove the subtraction entirely.

Run the test from Session Frontend in that state and it fails, red.

Open the row and there's your Message: "HP after damage doesn't match the expected value", along with the fact that the expected 80.0 came back as 100.0.

Seeing that red is half the reason to write tests. A test that's green from the start may be verifying nothing (a forgotten Assert connection, for instance).

Fix it, and go green

Restore the subtraction (CurrentHealth - Damage) and run again. Green.

The rule "an attack removes 20 HP" is now mechanically enforced. However much you change elsewhere, running this test confirms that one point is still intact, instantly.

Troubleshooting:

  • The test doesn't appear in the list → You haven't placed BP_Test_Damage in the test level. Creating the Blueprint isn't enough
  • It fails by timeout → You aren't calling Finish Test. Make sure every path reaches it
  • Assert Equal (Float) fails on a tiny differenceTolerance is 0.0. Set around 0.01
  • The enemy doesn't spawnSpawn Actor from Class's Transform is unset, or the Collision Handling setting is rejecting the spawn

Two things to take away.

  • Red first, then green: breaking it deliberately and watching it fail proves the test has teeth. Don't take a green result on faith
  • Put "where to look" in the Message: write it so you, six months from now, can start investigating immediately. That sentence is all you'll see in the results list

When you find and fix a bug, adding one test that reproduces it is highly effective. Once the Blueprint Debugger pins down the cause, encode those conditions as a test so you never step in the same hole twice.


Bonus: Good to Know for Later

Don't write too many. Chasing full coverage on a solo project means test maintenance eating your game-making time. Narrow it to what hurts when it breaks, and what has broken once. A good starting trio: damage math, saving and loading, progression flags — all easy to break unnoticed, all expensive when discovered late.

They run from the command line. You can run tests via executable arguments without opening Session Frontend, which enables "run everything every night" (CI). For solo work, though, build the habit of running them by hand first.

Terminology differs in localized UI. In the Japanese UI, Session Frontend and the Automation tab appear under translated names. Following English tutorials against a localized UI causes friction, so keeping the editor in English avoids a class of confusion (→ initial setup).

Run them before packaging. Running the suite once before packaging cuts down on "discovered it after shipping." With five tests, that's a few dozen seconds.


Summary

  • As features grow, manual checking inevitably gets skipped — and the skipped part breaks
  • UE has two kinds. Blueprint users start with Functional Test
  • A Functional Test is an Actor placed in a test level. Building one feels like building a game
  • Judge with the Assert nodes, and put "where to look" in the Message
  • Always call Finish Test or it fails by timeout
  • Turn it red once before you make it green. Distrust a test that starts green

You don't need to test everything. Start with the one place that hurts. Where in your game is a break hardest to notice?