자동화 테크니컬 가이드

프로그래머용 엔진 레벨 자동화 테스트 생성 방법 안내입니다.

Windows
MacOS
Linux

자동화 테스트

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 값 조합으로, 자동화 테스트 조건 및 동작을 지정하는 데 사용됩니다. 자세한 내용은 EAutomationTestFlagsEAutomationTestFlags 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 (복합 테스트)는 다수의 입력에 동일한 코드를 실행시키는 데 사용됩니다. 보통 콘텐츠 스트레스 테스트입니다. 예를 들어 모든 맵을 로드하거나 모든 블루프린트 컴파일을 하는 것이 복합 자동화 테스트에 적합할 것입니다. 참고로 RunTestGetTests 함수 둘 다 덮어써야 합니다. 다음은 프로젝트의 맵 전체 로딩을 테스트하는 예제입니다:

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 매크로를 사용하면 됩니다. 이 매크로는 ParamTypeParamName 으로 알려진 인수를 둘 더 받는데, 여기서 전달할 파라미터 이름과 유형을 정의합니다. 다음은 소스 컨트롤 프로바이더에 접속을 시작한 뒤 접속 시도가 끝날 때까지 기다리는 잠복성 명령 예제입니다:

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()));

데이터 로딩 또는 스트리밍에 관련되어 있거나, 한 프레임에 끝난다는 보장이 없는 작업이면 무엇이든 잠복성 명령을 사용하면 좋을 후보입니다. 예를 들어, 에디터에서 맵 로드는 즉시 일어나지만, 게임에서 맵 로드는 다음 프레임에 일어나므로, 잠복성 명령을 사용해야 자동화 테스트로 맵 로드가 필요할 때 작동방식 일관성을 확보할 수 있습니다.

새로운 언리얼 엔진 4 문서 사이트에 오신 것을 환영합니다!

문서 사이트에 대한 의견을 모을 수 있는 피드백 시스템을 포함해서 여러가지 새로운 기능을 준비하고 있습니다. 아래 Documentation Feedback 포럼(영문) 또는 언리얼 엔진 네이버 공식 카페(한글) 중 편하신 곳에 의견이나 문제점을 알려 주세요.

새 시스템이 준비되면 알려 드리겠습니다.

네이버 카페
공식 포럼