TDD for games 박일. TDD 란 ? Programmer Test 프로그래머가 직접 설치하는 자동화된...

24
TDD for games 박박

Transcript of TDD for games 박일. TDD 란 ? Programmer Test 프로그래머가 직접 설치하는 자동화된...

Page 1: TDD for games 박일. TDD 란 ? Programmer Test  프로그래머가 직접 설치하는 자동화된 테스트 White Box Test  QA 팀의 테스트는 Black Box Test.

TDD for games

박일

Page 2: TDD for games 박일. TDD 란 ? Programmer Test  프로그래머가 직접 설치하는 자동화된 테스트 White Box Test  QA 팀의 테스트는 Black Box Test.

TDD 란 ?

Programmer Test 프로그래머가 직접 설치하는 자동화된 테스트White Box Test QA 팀의 테스트는 Black Box Test

Page 3: TDD for games 박일. TDD 란 ? Programmer Test  프로그래머가 직접 설치하는 자동화된 테스트 White Box Test  QA 팀의 테스트는 Black Box Test.

불과 몇 분밖에 불과 몇 분밖에 걸리지 않는다걸리지 않는다 ..

테스트 실패테스트 실패

테스트 통과테스트 통과테스트 통과테스트 통과

체크 인체크 인

TDD 의 순환 과정

TEST (ShieldLevelStartsFull){ Shield shield; CHECK_EQUAL (Shield::kMaxLevel, shield.GetLevel());}

TEST (ShieldLevelStartsFull){ Shield shield; CHECK_EQUAL (Shield::kMaxLevel, shield.GetLevel());}

Shield::Shield() : m_level (Shield::kMaxLevel){}

Shield::Shield() : m_level (Shield::kMaxLevel){}

테스트작성 코드 작성

리팩토링

Page 4: TDD for games 박일. TDD 란 ? Programmer Test  프로그래머가 직접 설치하는 자동화된 테스트 White Box Test  QA 팀의 테스트는 Black Box Test.

왜 Test?

리니지 2 업데이트 일지 CHRONICLE 01 - 전란을 부르는 자들 CHRONICLE 02 - 풍요의 시대 CHRONICLE 03 - 눈뜨는 어둠 CHRONICLE 04 - 운명의 계승자들 CHRONICLE 05 - Death of Blood 혼돈의 왕좌 Interlude - 그 시작을 말하다 혼돈의 왕좌 - The kamael (2007)계속되는 업데이트 & 변경되는 기획리펙토링 ! 수정되었던 버그가 다시 출현 Code FreezeQA 팀의 업무 증가 현재 Lineage2 팀의 QA 팀 인원은 ? 최고의 QA 팀이 있어도 버그는 막을 수 없다 .

Page 5: TDD for games 박일. TDD 란 ? Programmer Test  프로그래머가 직접 설치하는 자동화된 테스트 White Box Test  QA 팀의 테스트는 Black Box Test.

UnitTest++

개발자 Noel Llopis Senior Architect High Moon Studios

Page 6: TDD for games 박일. TDD 란 ? Programmer Test  프로그래머가 직접 설치하는 자동화된 테스트 White Box Test  QA 팀의 테스트는 Black Box Test.

UnitTest++ 기능

TEST() TEST(AfterUserConnectToServerOnline) {CHECK() CHECK(0 < a.GetHP())CHECK_EQUAL() CHECK_EQUAL(true, a.IsOnline());CHECK_CLOSE() CHECK_CLOSE(15.42, a.GetAttackFactor(), 0.01);CHECK_ARRAY2D_CLOSE()

Page 7: TDD for games 박일. TDD 란 ? Programmer Test  프로그래머가 직접 설치하는 자동화된 테스트 White Box Test  QA 팀의 테스트는 Black Box Test.

UnitTest++ 기능FIXTURE TEST_FIXTURE JUnit 의 setUp, tearDown 과 같은 역할struct CharMaker {

Char tester;CharMaker() { tester = new Char(); }~CharMaker() { delete tester; }

TEST_FIXTURE(CharMaker, SomeThing...) {CHECK_EQUAL(true, tester.HasSomeThing());

TimeConstraint 실행 시간이 일정 이상 지나면 테스트 fail 로 간주 .TestResult r;TimeContraint t(10, result, TestDetails(“”, “”, “”, 0);TimeHelpers::SleepMs(20);CHECK_EQUAL(1, result.GetFailureCount());

Page 8: TDD for games 박일. TDD 란 ? Programmer Test  프로그래머가 직접 설치하는 자동화된 테스트 White Box Test  QA 팀의 테스트는 Black Box Test.

피보나치 예제

예제 보기

Page 9: TDD for games 박일. TDD 란 ? Programmer Test  프로그래머가 직접 설치하는 자동화된 테스트 White Box Test  QA 팀의 테스트는 Black Box Test.

Unit Test 예제World world;const initialHealth = 60;Player player(initialHealth);world.Add(&player, Transform(AxisY, 0, Vector3(10,0,10));HealthPowerup powerup;world.Add(&powerup, Transform(AxisY, 0, Vector3(-10,0,20);world.Update(0.1f);CHECK_EQUAL(initialHealth, player.GetHealth());

TEST (PlayersHealtDoesNotIncreaseWhileFarFromHealthPowerup) {World world;const initialHealth = 60;Player player(initialHealth);world.Add(&player, Transform(AxisY, 0, Vector3(10,0,10));HealthPowerup powerup;world.Add(&powerup, Transform(AxisY, 0, Vector3(-10,0,20);world.Update(0.1f);CHECK_EQUAL(initialHealth, player.GetHealth());

}

Page 10: TDD for games 박일. TDD 란 ? Programmer Test  프로그래머가 직접 설치하는 자동화된 테스트 White Box Test  QA 팀의 테스트는 Black Box Test.

최상의 관행 : 간결한 검사TEST (ShieldStartsAtInitialLevel){

ShieldComponent shield(100);CHECK_EQUAL (100, shield.GetLevel());

}

TEST (ShieldTakesDamage){

ShieldComponent shield(100);shield.Damage(30);CHECK_EQUAL (70, shield.GetLevel());

}

TEST (LevelCannotDropBelowZero){

ShieldComponent shield(100);shield.Damage(200);CHECK_EQUAL (0, shield.GetLevel());

}

TEST(ActorDoesntMoveIfPelvisBodyIsInSamePositionAsPelvisAnim){

component = ConstructObject<UAmpPhysicallyDrivableSkeletalComponent>();component->physicalPelvisHandle = NULL;component->SetOwner(owner);component->SkeletalMesh = skelMesh;component->Animations = CreateReadable2BoneAnimSequenceForAmpRagdollGetup(component, skelMesh,

10.0f, 0.0f);component->PhysicsAsset = physicsAsset;component->SpaceBases.AddZeroed(2);component->InitComponentRBPhys(false);component->LocalToWorld = FMatrix::Identity;const FVector actorPos(100,200,300);const FVector pelvisBodyPositionWS(100,200,380);const FTranslationMatrix actorToWorld(actorPos);owner->Location = actorPos;component->ConditionalUpdateTransform(actorToWorld);INT pelvisIndex = physicsAsset->CreateNewBody(TEXT("Bone1"));URB_BodySetup* pelvisSetup = physicsAsset->BodySetup(pelvisIndex);FPhysAssetCreateParams params = GetGenericCreateParamsForAmpRagdollGetup();physicsAsset->CreateCollisionFromBone( pelvisSetup,

skelMesh,1,params,boneThings);

URB_BodyInstance* pelvisBody = component->PhysicsAssetInstance->Bodies(0);NxActor* pelvisNxActor = pelvisBody->GetNxActor();SetRigidBodyPositionWSForAmpRagdollGetup(*pelvisNxActor, pelvisBodyPositionWS);

component->UpdateSkelPose(0.016f);component->RetransformActorToMatchCurrrentRoot(TransformManipulator());

const float kTolerance(0.002f);

FMatrix expectedActorMatrix;expectedActorMatrix.SetIdentity();expectedActorMatrix.M[3][0] = actorPos.X;expectedActorMatrix.M[3][1] = actorPos.Y;expectedActorMatrix.M[3][2] = actorPos.Z;const FMatrix actorMatrix = owner->LocalToWorld();CHECK_ARRAY2D_CLOSE(expectedActorMatrix.M, actorMatrix.M, 4, 4, kTolerance);

}

Page 11: TDD for games 박일. TDD 란 ? Programmer Test  프로그래머가 직접 설치하는 자동화된 테스트 White Box Test  QA 팀의 테스트는 Black Box Test.

예시 : 캐릭터의 행동TEST_F( CharacterFixture,

SupportedWhenLeapAnimationEndsTransitionsRunning ){

LandingState state(CharacterStateParameters(&character), AnimationIndex::LeapLanding);

state.Enter(input);input.deltaTime = character.GetAnimationDuration(

AnimationIndex::LeapLanding ) + kEpsilon;

character.supported = true;CharacterStateOutput output = state.Update( input );CHECK_EQUAL(std::string("TransitionState"),

output.nextState->GetClassInfo().GetName());const TransitionState& transition = *output.nextState;CHECK_EQUAL(std::string("RunningState"),

transition.endState->GetClassInfo().GetName());}

Page 12: TDD for games 박일. TDD 란 ? Programmer Test  프로그래머가 직접 설치하는 자동화된 테스트 White Box Test  QA 팀의 테스트는 Black Box Test.

Working Effectively with Legacy Code

DebuggingRegression Test

Page 13: TDD for games 박일. TDD 란 ? Programmer Test  프로그래머가 직접 설치하는 자동화된 테스트 White Box Test  QA 팀의 테스트는 Black Box Test.

Test Driven Debugging?

일반적인 디버깅 방법은 ?1. 버그 리포트 시스템에 새로운 버그 추가2. 게임 스크립트 데이타 받아서 컴파일3. 서버들 빌드 후 loading

1. 최소의 셋팅으로도 5~10 분은 걸림 .4. 클라이언트 1 개 ~3 개 실행

1. 역시나 3 분 이상 소모됨5. 재현

1. 재현하기 힘든 경우라면 ? 2. 좋은 버프만 동시에 30 개를 받는 경우는 ?

6. 코드 수정7. 3 번으로 돌아가서 확인

Page 14: TDD for games 박일. TDD 란 ? Programmer Test  프로그래머가 직접 설치하는 자동화된 테스트 White Box Test  QA 팀의 테스트는 Black Box Test.

Test Driven Debugging!!

TDD 를 이용할 때1. 디버그 관리자에 새로운 버그 추가2. 게임 스크립트 데이타 받아서 컴파일3. 서버들 빌드 후 loading

1. 최소의 셋팅으로도 5~10 분은 걸림 .2. 스크립트 없이 테스트 할 수 있는 경우가 많음 .

4. 클라이언트 1 개 ~3 개 실행1. 역시나 3 분 이상 소모됨2. 클라이언트 없이 실행 가능 .

5. 재현1. 재현하기 힘든 경우라면 ?2. 좋은 버프만 동시에 30 개를 받는 경우는 ?3. 직접 확률을 지정하거나 , 코드에서 loop 돌릴 수 있다 .

6. 코드 수정7. 3 번으로 돌아가서 확인

8. 한 번 만들어진 테스트는 계속 남는다 .

Page 15: TDD for games 박일. TDD 란 ? Programmer Test  프로그래머가 직접 설치하는 자동화된 테스트 White Box Test  QA 팀의 테스트는 Black Box Test.

Regression Test

변경되지 않은 기능은 ‘예전과 동일하게 동작함’을 보장하는 테스트 Characterization Test 현재 상태를 그대로 테스트로 추가CUser* pMe = ...;CHECK_EQUAL(0, pMe->GetLife()); // Test FailedCHECK_EQUAL(644, pMe->GetLife()); // Test 성공

리펙토링을 하기 전 필수적인 작업버그가 생기면 수익 감소 웹진에 소식으로 올라올 수도 ?!

Page 16: TDD for games 박일. TDD 란 ? Programmer Test  프로그래머가 직접 설치하는 자동화된 테스트 White Box Test  QA 팀의 테스트는 Black Box Test.

테스트 방법

리턴값CHECK_CLOSE(10.5248, CAttacker::GetCritical(p1, p

2, ...), 0.001);객체 상태pUser->GetSkill(1, 1);CHECK_EQUAL(1, pUser->GetSkillNum());객체 상호작용 Mock 객체 사용 .

Page 17: TDD for games 박일. TDD 란 ? Programmer Test  프로그래머가 직접 설치하는 자동화된 테스트 White Box Test  QA 팀의 테스트는 Black Box Test.

TDD Tips

#if defined(USING_TDD) && defined(_DEBUG) 팀원들을 안심시켜라 . Release 빌드에서는

file 에서 오른쪽 버튼 -> general 탭 에서 exclude file from build테스트를 빠르게 유지 파일 I/O 를 최소화한다 . TDD 돌릴 것인지 여부를 ini 파일로 결정test 없는 private 보다 test 있는 public 이 안전멤버변수도 파라메타로 넘기면 test 만들기 쉬워진다 . 마찬가지로 전역변수도 파라메타로 넘겨주자 .이제 아예 static 멤버함수로 만들자 . 좀 더 쉽게 테스트를 만들 수 있다 .breakpoint -> trace 는 쓰지 말자 . 대신 모든 검사에 CHECK 를 이용한다 .임의성 테스트Windows 프로그램에서 콘솔 띄우기

Page 18: TDD for games 박일. TDD 란 ? Programmer Test  프로그래머가 직접 설치하는 자동화된 테스트 White Box Test  QA 팀의 테스트는 Black Box Test.

임의성 테스트타격 크리티컬 같이 random 값이 들어가는 계산은 어떻게 테스트 할 수 있을까 ?

#if defined(_DEBUG) && defined(USING_TDD)if (IsSetRandomNumber()) {

return GetTDDRandomNumber();}

#endif

double GetTDDRandomNumber() {return MyTestUnit::Inst().m_Random;

}TEST_FIXTURE(FixtureUser2, CheckMagicCritical){

int userLevel = 60;const double bonus = 50.0;MyTestUnit ::Inst().m_Random = 100.0; // 무조건 성공시키겠다 .CHECK_EQUAL(true, GetMagicCritical(user, userLevel, bonus));MyTestUnit ::Inst().m_Random = 0.0; // 무조건 실패시키겠다 .CHECK_EQUAL(false, GetMagicCritical(...));

Page 19: TDD for games 박일. TDD 란 ? Programmer Test  프로그래머가 직접 설치하는 자동화된 테스트 White Box Test  QA 팀의 테스트는 Black Box Test.

Windows 프로그램에서 콘솔 띄우기// http://dslweb.nwnexus.com/~ast/dload/guicon.htmstatic const WORD MAX_CONSOLE_LINES = 500;void RedirectIOToConsole() {

CONSOLE_SCREEN_BUFFER_INFO coninfo;AllocConsole();GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo);coninfo.dwSize.Y = MAX_CONSOLE_LINES;SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize);lStdHandle = (long)GetStdHandle(STD_OUTPUT_HANDLE);hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);

// redirect unbuffered STDIN to the consolelStdHandle = (long)GetStdHandle(STD_INPUT_HANDLE);hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);lStdHandle = (long)GetStdHandle(STD_ERROR_HANDLE);hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);fp = _fdopen( hConHandle, "w" );*stderr = *fp;setvbuf( stderr, NULL, _IONBF, 0 );ios::sync_with_stdio();

}FreeConsole() 이용

Page 20: TDD for games 박일. TDD 란 ? Programmer Test  프로그래머가 직접 설치하는 자동화된 테스트 White Box Test  QA 팀의 테스트는 Black Box Test.

Two Stage Test

1단계 리소스 로딩 이전에 로직 테스트 , 순수한 의미의 UnitTest2단계 리소스 로딩 후에 월드 지형 버그 , 스킬 , 퀘스트 등 데이터 로딩이

필요한 테스트 지형의 이동 가능 여부 등Suite, TestList 와 CHECK_EX 를 이용하면 two stage test 도 가능하다 .

Page 21: TDD for games 박일. TDD 란 ? Programmer Test  프로그래머가 직접 설치하는 자동화된 테스트 White Box Test  QA 팀의 테스트는 Black Box Test.

Mock 객체

소켓 통신을 어떻게 테스트할 것인가 ?파일 시스템이 꽉 차 있는 경우는 어떻게 테스트 할 것인가 ? 진짜 하드를 꽉 채운 후 테스트 ?

DB 관련원하는 환경을 가짜로 돌아가는 것처럼 만들어 주는 객체를 이용하자 .

Page 22: TDD for games 박일. TDD 란 ? Programmer Test  프로그래머가 직접 설치하는 자동화된 테스트 White Box Test  QA 팀의 테스트는 Black Box Test.

Mock 객체class SecretObject {

protected:int m_Age;virtual int GetMyAge() const { return m_Age; }

}class MockSecretObject : public SecretObject {

public:using SecretObject::m_Age;virtual int GetMyAge() const {

return SecretObject::GetMyAge(); }

}

MockSecretObject a;a.GrownUp();CHECK_EQUAL(1, a.GetMyAge());CHECK_EQUAL(1, a.m_Age);

Page 23: TDD for games 박일. TDD 란 ? Programmer Test  프로그래머가 직접 설치하는 자동화된 테스트 White Box Test  QA 팀의 테스트는 Black Box Test.

Mock 객체class CMockPlayer : public CPlayer {

virtual CSocket* GetSocket() { return m_pSocket; }CMockSocket* m_pSocket;void Attack(double damage) {

GetSocket()->SendMsg(“You got damage %d”, damage);

}class CMockSocket : public CSocket {

virtual void Send(...) {}virtual bool SendMsg(…) { return true;}

}

Page 24: TDD for games 박일. TDD 란 ? Programmer Test  프로그래머가 직접 설치하는 자동화된 테스트 White Box Test  QA 팀의 테스트는 Black Box Test.

참고자료

http://unittest-cpp.sourceforge.net/ UnitTest++ UnitTest++ 소스 받는 곳소스 받는 곳http://www.gamesfromwithin.com Noel LlopisNoel Llopis - - [email protected] GDC2006 GDC2006 발표자료발표자료

http://andstudy.com/andwiki/wiki.php/BackwardsIsForward 위 자료를 번역해 놓은 위 자료를 번역해 놓은 PPT PPT 및 노및 노트트