How do I manage UObjects during unit testing?

I’m trying to write unit tests for the first time, using “Automation Spec” (Automation Spec | Unreal Engine 4.27 Documentation).

The code that I want to test defines several UCLASS types inheriting from UObject, but none of the code requires a scene or anything weird to execute. It’s just manipulating array data. The main reason it’s a UCLASS is so that it’s accessible to Blueprints.

However, during unit tests, there’s nothing I can attach these UObjects to, which means they could be destroyed by the GC while the tests are still running. So, how do I create and manage transient UObjects during unit tests?

1 Like

I want to know this too.
What I have been doing (which I don’t know if it’s correct or not) is creating a new map in BeforeEach and destoying it in AfterEach:

UWorld* World = FAutomationEditorCommonUtils::CreateNewMap();

Then you can create actors, objects, etc.

But I don’t know if this approach has any limitations or whatnot.

You should be able to add the UObjects as root objects so they won’t be garbage collected. Just make sure you have some kind of RAII structure in place that removes the objects from the root set again at the end of the test.

1 Like

My friend introduced me to TStrongObjectPtr, which appears to be a shared pointer for UObjects. This might provide what we want.

UnrealEverything also mentioned that we can simply mark the UObjects as “root” objects when creating them, so that the GC doesn’t try to collect them. However, this also means we have to remember to clean them up afterwards.

1 Like

FWIW I don’t think there is anything wrong with creating new maps for each test, particularly if your tests might interact with the world. Doing so ensures that each test has a consistent world state every time it runs.

The TStrongObjectPtr is a great tip, I’ll keep it in mind for the cases where a shared world isn’t risking inconsistent test results. Thanks!

To add to my previous answer, I think FAutomationEditorCommonUtils only works in editor.

Using UWorld::CreateWorld(EWorldType::Game, false); might be a better option to create a temporary world, spawn some things into it and destroying it using World->DestroyWorld(false);

Should it be better to use EWorldType::Inactive as it is not being loaded to the editor?

EngineTypes.h:

/** An editor world that was loaded but not currently being edited in the level editor */
Inactive