자동화 테스트
Automation Testing (자동화 테스트)는 자동화 테스트 가장 밑단에 있는 것으로, 언리얼 엔진 코어 기능의 로우 레벨 테스트에 적합합니다. 이 시스템은 UObject 생태계 밖에 존재하므로, 블루프린트 또는 엔진의 리플렉션 시스템 에 보이지 않습니다. 이 테스트는 내장된 코드로 에디터 안에서나 운영 체제의 명령줄 파라미터로 콘솔 명령줄 에서 실행시킬 수 있습니다. 자동화 테스트는 Simple(단순) 과 Complex(복합), 두 가지 유형으로 나눌 수 있습니다. 두 유형 모두 FAutomationTestBase 파생 클래스로 구현됩니다.
새 자동화 테스트 생성
자동화 테스트는 매크로로 선언하고, FAutomationTestBase 클래스에서 가상 함수를 덮어쓰는 것으로 구현합니다. 단순 테스트는 IMPLEMENT_SIMPLE_AUTOMATION_TEST 매크로로 선언하는 반면, 복합 테스트는 IMPLEMENT_COMPLEX_AUTOMATION_TEST 매크로가 필요합니다. 두 매크로 모두 똑같이 다음 순서대로 세 가지 파라미터를 활용합니다:
| 파라미터 | 설명 |
|---|---|
TClass |
원하는 테스트 클래스 이름입니다. 매크로는 이 이름의 클래스를 FPlaceholderTest 같은 식으로 생성하며, 여기에 테스트를 구현하면 됩니다. |
PrettyName |
UI 에 표시되는 계층형 테스트 이름을 나타내는 문자열입니다. 예를 들면 아래 최소 예제의 "TestGroup.TestSubgroup.Placeholder Test" 식입니다. |
TFlags |
EAutomationTestFlags 값 조합으로, 자동화 테스트 조건 및 동작을 지정하는 데 사용됩니다. 자세한 내용은 EAutomationTestFlags API Page를 참고하세요. |
새로운 자동화 테스트 클래스를 두 매크로 중 하나로 선언한 후, 그 함수 기능을 구현하면 됩니다. 작성해야 하는 함수는 다음과 같습니다:
| 함수 | 파라미터 | 설명 |
|---|---|---|
RunTest |
실제 테스트를 수행하는 함수로, 테스트에 통과했으면 true 를 반환하고, 실패했으면 false 를 반환하여 알립니다. |
|
Parameters |
이 파라미터는 파싱하거나 특정 펑셔널 테스트에 적합한 다른 함수로 전달할 수 있습니다. | |
GetTests |
복합 테스트용으로만 덮어써야 하는 함수입니다. 단순 테스트는 이 함수의 자동 생성 버전이 선언 매크로에 내장되어 있습니다. | |
OutBeautifiedNames |
각 테스트 별로 UI 에 보이는 PrettyName 으로 채워야 하는 스트링 배열입니다. |
|
OutTestCommands |
OutBeautifiedNames 에 평행일 것으로 기대되는 스트링 배열로, RunTest 에 전달할 Parameters 로 채워야 합니다. |
소스 파일 위치
현재 규칙은 모든 자동화 테스트를 관련된 모듈 내 Private\Tests 디렉터리에 넣는 것입니다. 자동화 테스트가 특정 클래스에 1:1 일치하면, 테스트 파일 이름을 [ClassFilename]Test.cpp 로 지어주세요. 예를 들어 FText 에만 적용되는 테스트는 TextTest.cpp 에 작성해야 할 것입니다.
최소 예제
가장 작고 단순한 테스트 예제라면 자동으로 성공( 또는 실패)하는 단순 테스트일 것입니다. 이런 테스트를 만들어 보는 것은 테스트 구성이 올바른지 확인하는 첫 단계로 유용할 것이며, 이후 보다 의미있는 테스트를 만들면 됩니다.
IMPLEMENT_SIMPLE_AUTOMATION_TEST(FPlaceholderTest, "TestGroup.TestSubgroup.Placeholder Test", EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
bool FPlaceholderTest::RunTest(const FString& Parameters)
{
// true 를 반환하면 테스트 통과, false 를 반환하면 테스트 실패입니다.
return true;
}
단순 테스트
Simple Tests (단순 테스트) 는 단일 원자성 테스트를 말하며, 유닛 또는 피처 테스트에 좋습니다. 예를 들면 단순 테스트를 통해 현재 맵이 에디터에서 플레이 모드에 로드되는지, 또는 슬레이트에서 텍스트 줄바꿈이 제대로 되는지 테스트할 수 있습니다. SetRes 명령의 함수 기능을 테스트하는 예제입니다:
IMPLEMENT_SIMPLE_AUTOMATION_TEST( FSetResTest, "Windows.SetResolution", ATF_Game )
bool FSetResTest::RunTest(const FString& Parameters)
{
FString MapName = TEXT("AutomationTest");
FEngineAutomationTestUtilities::LoadMap(MapName);
int32 ResX = GSystemSettings.ResX;
int32 ResY = GSystemSettings.ResY;
FString RestoreResolutionString = FString::Printf(TEXT("setres %dx%d"), ResX, ResY);
ADD_LATENT_AUTOMATION_COMMAND(FEngineWaitLatentCommand(2.0f));
ADD_LATENT_AUTOMATION_COMMAND(FExecStringLatentCommand(TEXT("setres 640x480")));
ADD_LATENT_AUTOMATION_COMMAND(FEngineWaitLatentCommand(2.0f));
ADD_LATENT_AUTOMATION_COMMAND(FExecStringLatentCommand(RestoreResolutionString));
return true;
}
복합 테스트
Complex Tests (복합 테스트) 는 다수의 입력에 동일한 코드를 실행시키는 데 사용됩니다. 보통 콘텐츠 스트레스 테스트입니다. 예를 들어 모든 맵을 로드하거나 모든 블루프린트 컴파일을 하는 것이 복합 자동화 테스트에 적합할 것입니다. 참고로 RunTest 및 GetTests 함수 둘 다 덮어써야 합니다. 다음은 프로젝트의 맵 전체 로딩을 테스트하는 예제입니다:
IMPLEMENT_COMPLEX_AUTOMATION_TEST(FLoadAllMapsInGameTest, "Maps.LoadAllInGame", ATF_Game)
void FLoadAllMapsInGameTest::GetTests(TArray<FString>& OutBeautifiedNames, TArray <FString>& OutTestCommands) const
{
FEngineAutomationTestUtilities Utils;
TArray<FString> FileList;
FileList = GPackageFileCache->GetPackageFileList();
// 모든 파일을 대상으로, 확장자가 map 인 파일을 추가하면서 반복처리합니다.
for( int32 FileIndex=0; FileIndex< FileList.Num(); FileIndex++ )
{
const FString& Filename = FileList[FileIndex];
// MAPSONLY 모드인 경우 확장자가 map 이 아닌 파일명은 무시합니다.
if ( FPaths::GetExtension(Filename, true) == FPackageName::GetMapPackageExtension() )
{
if (!Utils.ShouldExcludeDueToPath(Filename))
{
OutBeautifiedNames.Add(FPaths::GetBaseFilename(Filename));
OutTestCommands.Add(Filename);
}
}
}
}
bool FLoadAllMapsInGameTest::RunTest(const FString& Parameters)
{
FString MapName = Parameters;
FEngineAutomationTestUtilities::LoadMap(MapName);
ADD_LATENT_AUTOMATION_COMMAND(FEnqueuePerformanceCaptureCommands());
return true;
}
Parameters 인수는 테스트 상황에 맞게끔 필요한 방식으로 만들어 파싱할 수 있습니다. 복합 테스트의 경우, 같은 코드를 사용하여 여러 데이터 포인트를 테스트하기 위한 방편으로 의도된 것입니다.잠복성 명령
Latent Commands (잠복성 명령)은 RunTest 도중 대기열에 등록시킬 수 있는 것으로, 코드 일정 부분이 여러 프레임에 걸쳐 실행되도록 합니다. Latent Action (잠복성 동작)을 만들기 위해서는, DEFINE_LATENT_AUTOMATION_COMMAND 매크로를 통해 정의해야 합니다. 이 매크로는 CommandName 이라 알려진 파라미터를 하나 받는데, 여기서 이 유형의 잠복성 명령에 대해 생덩되는 클래스 이름을 정의합니다. 잠복성 명령 생성을 마치기 위해서는, 새 클래스가 자신의 Update 함수용 본문이 있어야 합니다. Unit Test Manager (유닛 테스트 매니저)가 테스트 실행을 마칠 때까지 실행되는 단순한 잠복성 명령 예제입니다:
DEFINE_LATENT_AUTOMATION_COMMAND(FNUTWaitForUnitTests);
bool FNUTWaitForUnitTests::Update()
{
return GUnitTestManager == NULL || !GUnitTestManager->IsRunningUnitTests();
}
생성하고자 하는 잠복성 명령이 파라미터 스트링과 같은 인수를 요하는 경우, DEFINE_LATENT_AUTOMATION_COMMAND 매크로를 사용하면 됩니다. 이 매크로는 ParamType 및 ParamName 으로 알려진 인수를 둘 더 받는데, 여기서 전달할 파라미터 이름과 유형을 정의합니다. 다음은 소스 컨트롤 프로바이더에 접속을 시작한 뒤 접속 시도가 끝날 때까지 기다리는 잠복성 명령 예제입니다:
DEFINE_LATENT_AUTOMATION_COMMAND_ONE_PARAMETER(FConnectLatentCommand, SourceControlAutomationCommon::FAsyncCommandHelper, AsyncHelper);
bool FConnectLatentCommand::Update()
{
// 로그인 시도 후 결과를 기다립니다.
if(!AsyncHelper.IsDispatched())
{
if(ISourceControlModule::Get().GetProvider().Login( FString(), EConcurrency::Asynchronous, FSourceControlOperationComplete::CreateRaw( &AsyncHelper, &SourceControlAutomationCommon::FAsyncCommandHelper::SourceControlOperationComplete ) ) != ECommandResult::Succeeded)
{
return false;
}
AsyncHelper.SetDispatched();
}
return AsyncHelper.IsDone();
}
Update 함수가 true 를 반환하면, 잠복성 명령이 완료된 것으로 간주합니다. false 를 반환하면 자동화 테스트 실행을 즉시 중지하고 다음 프레임에 재시도해야 함을 나타냅니다. 테스트 코드에서 잠복성 명령을 사용하려면, 실행하고자 하는 잠복성 명령 생성자를 포함시킨 ADD_LATENT_AUTOMATION_COMMAND 를 부르면(invoke) 됩니다. 파라미터를 가진 잠복성 명령이 생겼으면(establish), 파라미터가 받아야 하는 값을 생성자 인수로 전달합니다. 다음은 RunTest 함수에서, 모든 유닛 테스트 완료를 기다린 뒤, 기존 이름의 소스 컨트롤 프로바이더에 접속 시도하는 코드입니다:
ADD_LATENT_AUTOMATION_COMMAND(FNUTWaitForUnitTests());
ADD_LATENT_AUTOMATION_COMMAND(FConnectLatentCommand(SourceControlAutomationCommon::FAsyncCommandHelper()));