UE4 supports testing as part of its development environment, allowing you to write automated tests without needing separate tools.
Furthermore, by using Latent Commands, you can describe tests that span multiple frames, which is quite convenient.
However, when trying to perform delayed evaluation using multiple Latent Commands, I always found it cumbersome that I had to define a DEFINE_LATENT_AUTOMATION_COMMAND
macro for each type of evaluation I wanted to perform.
For example, something like this:
DEFINE_LATENT_AUTOMATION_COMMAND_TWO_PARAMETER(FHogeObjCommand, FAutomationTestBase*, Test, UMyObject*, Obj);
bool FHogeObjCommand::Update()
{
check(Test);
check(Obj);
Test->TestEqual(TEXT("check"), Obj->Property, 1);
return true;
}
DEFINE_LATENT_AUTOMATION_COMMAND_TWO_PARAMETER(FHogeObjCommand2, FAutomationTestBase*, Test, UMyObject*, Obj);
bool FHogeObjCommand2::Update()
{
check(Test);
check(Obj);
Obj->Hoge();
return true;
}
IMPLEMENT_SIMPLE_AUTOMATION_TEST(FHogeTest, "Hoge.HogeTest", EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
bool FHogeTest::RunTest(const FString& Parameters)
{
auto obj = NewObject<UMyObject>();
ADD_LATENT_AUTOMATION_COMMAND(FEngineWaitLatentCommand(1.0f));
ADD_LATENT_AUTOMATION_COMMAND(FHogeObjCommand(this, obj));
ADD_LATENT_AUTOMATION_COMMAND(FHogeObjCommand2(this, obj));
ADD_LATENT_AUTOMATION_COMMAND(FEngineWaitLatentCommand(1.0f));
ADD_LATENT_AUTOMATION_COMMAND(FHogeObjCommand(this, obj));
return true; // Added return true
}
So, tired of defining multiple commands every time, I defined the following Latent Command, and it seems to work for almost everything.
DEFINE_LATENT_AUTOMATION_COMMAND_ONE_PARAMETER(FLambdaCommand, TFunction<void()>, InWork);
bool FLambdaCommand::Update()
{
InWork();
return true;
}
Using this Latent Command, the previous test can be written as:
IMPLEMENT_SIMPLE_AUTOMATION_TEST(FHogeTest, "Hoge.HogeTest", EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
bool FHogeTest::RunTest(const FString& Parameters)
{
auto obj = NewObject<UMyObject>();
ADD_LATENT_AUTOMATION_COMMAND(FEngineWaitLatentCommand(1.0f));
ADD_LATENT_AUTOMATION_COMMAND(FLambdaCommand([this, &obj]()
{
TestEqual(TEXT("check"), obj->Property, 1);
})); // Corrected closing parenthesis
ADD_LATENT_AUTOMATION_COMMAND(FLambdaCommand([&obj]()
{
obj->Hoge();
})); // Corrected closing parenthesis
ADD_LATENT_AUTOMATION_COMMAND(FEngineWaitLatentCommand(1.0f));
ADD_LATENT_AUTOMATION_COMMAND(FLambdaCommand([this, &obj]()
{
TestEqual(TEXT("check"), obj->Property, 1);
})); // Corrected closing parenthesis
return true; // Added return true
}
I prefer this way as it makes the flow of processing easier to follow.
However, while casually browsing the Engine source code, I found that a similar command already existed without needing to create it myself. I missed it because other Latent Commands were grouped in AutomationCommon.h. I wonder if it existed before…
// AutomationTest.h
class FFunctionLatentCommand : public IAutomationLatentCommand
{
public:
FFunctionLatentCommand(TFunction<bool()> InLatentPredicate)
: LatentPredicate(MoveTemp(InLatentPredicate))
{
}
virtual ~FFunctionLatentCommand()
{
}
virtual bool Update() override
{
return LatentPredicate();
}
private:
TFunction<bool()> LatentPredicate;
};