생산적인 개발을 위한 지속적인 테스트
TRANSCRIPT
![Page 2: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/2.jpg)
효율적으로효율적으로개발하려면개발하려면??개발하려면개발하려면??
![Page 3: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/3.jpg)
낭비낭비 제거제거
![Page 4: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/4.jpg)
빠른빠른 피드백이피드백이 중요중요
![Page 5: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/5.jpg)
CruiseControl.NETCruiseControl.NET
![Page 6: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/6.jpg)
CruiseControl.NETCruiseControl.NET
![Page 7: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/7.jpg)
CruiseControl.NETCruiseControl.NET이이 하는하는 일일• 소스 빌드• Asset 통합• 유닛 테스트통합 테스트 등의 각종 테스트• 통합 테스트 등의 각종 테스트
• 코드 정적 분석• 크래쉬 덤프 분석• 문서화(doxygen)• 배포
![Page 8: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/8.jpg)
테스트에테스트에노력을노력을 기울이면기울이면??노력을노력을 기울이면기울이면??
![Page 9: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/9.jpg)
1. 1. 위험을위험을 감소시키고감소시키고 품질을품질을 높인다높인다..
2. 2. 변경에변경에 강해진다강해진다..
33. . 프로젝트프로젝트 가시성을가시성을 높이고높이고, , 자신감을자신감을 얻을얻을 수수 있게있게 한다한다..
![Page 10: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/10.jpg)
테스트테스트 결과결과 로그로그
<?xml version="1.0"?><maiettest-results tests=“2" failedtests=“1" failures="1" time="0.137">
<report text="time : 680 sec" /><report text=“총 클라이언트 개수 : 4650" /><test name=“로그인 반복" time="0.062" >
<success message="success" /><success message="success" /></test><test name=“캐릭터 생성 반복" time="0.062" >
<failure message="Crash!" /></test>
</maiettest-results>
![Page 11: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/11.jpg)
테스트테스트 결과결과 로그로그
• 테스트 결과를 XML로 만들고, XSL을 이용하여 CruseControl.NET에 출력한다.
![Page 12: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/12.jpg)
FeedbackFeedback
![Page 13: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/13.jpg)
사례사례
![Page 14: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/14.jpg)
Unit TestUnit Test• 코드에 대한 논리적인 검사• 모든 테스트 중에서 제일 중요하
다.
![Page 15: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/15.jpg)
Unit TestTEST(TestMathFunctionTruncateToInt)
{
CHECK_EQUAL(0, GMath::TruncateToInt(0.0));
CHECK_EQUAL(5, GMath::TruncateToInt(5.6));
CHECK_EQUAL(13, GMath::TruncateToInt(13.2));
CHECK_EQUAL(13, GMath::TruncateToInt(13.2));
CHECK_EQUAL(-6, GMath::TruncateToInt(-5.6));CHECK_EQUAL(-6, GMath::TruncateToInt(-5.6));
CHECK_EQUAL(-3, GMath::TruncateToInt(-2.1));
}
![Page 16: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/16.jpg)
Unit TestUnit TestTEST(ShieldCanBeDamaged)
{
World world;
world.Create();
Player player;
player.Create(world, vec3(1000,1000,0));
player.SetHealth(1000);player.SetHealth(1000);
Shield shield;
shield.SetHealth(100);
player.Equip(shield);
player.Damage(200);
CHECK(shield.GetHealth() == 0);
CHECK(player.GetHealth() == 900);
}
![Page 17: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/17.jpg)
Unit TestUnit Test
![Page 18: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/18.jpg)
Unit TestUnit TestTEST_FIXTURE(FLogin, TestLogin_MC_COMM_REQUEST_LOGIN_SERVER_Success){
TD_LOGIN_INFOTD_LOGIN_INFOTD_LOGIN_INFOTD_LOGIN_INFO tdLoginInfotdLoginInfotdLoginInfotdLoginInfo = = = = MakeParam_TD_LOGIN_INFOMakeParam_TD_LOGIN_INFOMakeParam_TD_LOGIN_INFOMakeParam_TD_LOGIN_INFO();();();();OnRecv_MMC_COMM_REQUEST_LOGIN_SERVEROnRecv_MMC_COMM_REQUEST_LOGIN_SERVEROnRecv_MMC_COMM_REQUEST_LOGIN_SERVEROnRecv_MMC_COMM_REQUEST_LOGIN_SERVER((((m_nRequestIDm_nRequestIDm_nRequestIDm_nRequestID, , , , m_nConnectionKeym_nConnectionKeym_nConnectionKeym_nConnectionKey, &, &, &, &tdLoginInfotdLoginInfotdLoginInfotdLoginInfo););););
// 로그인 하기 전의 값 체크CHECK_EQUAL(0, gmgr.pPlayerObjectManager->GetPlayersCount());
// 클라이언트로부터 존 입장 패킷 받음OnRecv_MC_COMM_REQUEST_LOGIN_SERVEROnRecv_MC_COMM_REQUEST_LOGIN_SERVEROnRecv_MC_COMM_REQUEST_LOGIN_SERVEROnRecv_MC_COMM_REQUEST_LOGIN_SERVER((((m_nConnectionKeym_nConnectionKeym_nConnectionKeym_nConnectionKey););););OnRecv_MC_COMM_REQUEST_LOGIN_SERVEROnRecv_MC_COMM_REQUEST_LOGIN_SERVEROnRecv_MC_COMM_REQUEST_LOGIN_SERVEROnRecv_MC_COMM_REQUEST_LOGIN_SERVER((((m_nConnectionKeym_nConnectionKeym_nConnectionKeym_nConnectionKey););););
// 마스터 서버로 인증키 확인 패킷 보냈는지 체크CHECK_EQUALCHECK_EQUALCHECK_EQUALCHECK_EQUAL(MC_COMM_RESPONSE_LOGIN_SERVER, (MC_COMM_RESPONSE_LOGIN_SERVER, (MC_COMM_RESPONSE_LOGIN_SERVER, (MC_COMM_RESPONSE_LOGIN_SERVER, m_pLinkm_pLinkm_pLinkm_pLink---->>>>GetCommandIDGetCommandIDGetCommandIDGetCommandID(0));(0));(0));(0));CHECK_EQUALCHECK_EQUALCHECK_EQUALCHECK_EQUAL(RESULT_SUCCESS, (RESULT_SUCCESS, (RESULT_SUCCESS, (RESULT_SUCCESS, m_pLinkm_pLinkm_pLinkm_pLink---->>>>GetParamGetParamGetParamGetParam<<<<intintintint>(0, 0));>(0, 0));>(0, 0));>(0, 0));CHECK_EQUALCHECK_EQUALCHECK_EQUALCHECK_EQUAL((((m_pLinkm_pLinkm_pLinkm_pLink---->>>>GetUIDGetUIDGetUIDGetUID(), (), (), (), m_pLinkm_pLinkm_pLinkm_pLink---->>>>GetParamGetParamGetParamGetParam<<<<MUIDMUIDMUIDMUID>(0, 1));>(0, 1));>(0, 1));>(0, 1));
// 로그인 후 값 체크CHECK_EQUAL(1, gmgr.pPlayerObjectManager->GetPlayersCount());
GPlayerObject* pPlayerObject = gmgr.pPlayerObjectManager->GetPlayer(m_pLink->GetUID());CHECK(pPlayerObject != NULL);CHECK_EQUAL(m_nGUID, pPlayerObject->GetAccountInfo().nGUID);CHECK_EQUAL(string(“birdkr”), string(pPlayerObject->GetAccountInfo().strID));
}
![Page 19: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/19.jpg)
Mock ObjectMock Objectclass MockPlayer : public GPlayer
{
public:
MockPlayer() {};
virtual ~ MockPlayer() {};
…
virtual void SendToThisSector (MPacket* pPacket) override { }virtual void SendToThisSector (MPacket* pPacket) override { }
virtual void SendToMe(MPacket * pPacket) override { }
virtual void SendToGuild(MPacket* pPacket) override { }
…
};
![Page 20: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/20.jpg)
타이밍타이밍, , 랜덤랜덤class XSystem
{
public:
virtual unsigned int GetNowTime()
{
return timeGetTimetimeGetTimetimeGetTimetimeGetTime()()()(); return timeGetTimetimeGetTimetimeGetTimetimeGetTime()()()();
}
virtual int RandomNumber(int nMin, int nMax)
{
return (randrandrandrand()()()() % (nMax - nMin + 1)) + nMin;
}
};
![Page 21: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/21.jpg)
타이밍타이밍, , 랜덤랜덤class MockSystem : public XSystem
{
protected:
unsigned int m_nExpectedNowTime;
public:
virtual unsigned int GetNowTime()
{{
if (m_nExpectedNowTime != 0)
return m_nExpectedNowTime;
return XSystem::GetNowTime();
}
void ExpectNowTime(unsigned int nNowTime)
{
m_nExpectedNowTime = nNowTime;
}
};
![Page 22: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/22.jpg)
타이밍타이밍, , 랜덤랜덤TEST_FIXTURE(FPlayerInOut2, TestObjectCacheDelete)
{
vec3 vNewPos = vec3(100.0f, 100.0f, 0.0f);
CHECK_EQUAL(2, gg.omgr->GetCount());
m_pNet->OnRecv( MC_ENTITY_WARP, 3, NEW_ID(m_pMyPlayer->GetID
Update(0.1f);
CHECK_CLOSE(100.0f, m_pMyPlayer->GetPosition().x, 0.001f);
CHECK_CLOSE(100.0f, m_pMyPlayer->GetPosition().y, 0.001f);
XExpectNowTime(XGetNowTime() + 10000 );
Update(10.0f);
// 멀리 있는 다른 플레이어가 지워졌다.
CHECK_EQUAL(1, gg.omgr->GetCount());
}
![Page 23: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/23.jpg)
싱글턴싱글턴, , 전역전역 변수변수template <class Type>
class GTestMgrWrapper : public MInstanceChanger<Type>
{
public:
GTestMgrWrapper() : MInstanceChanger()
{
m_pOriginal = gmgr.Change(m_pTester);m_pOriginal = gmgr.Change(m_pTester);
}
~GTestMgrWrapper()
{
gmgr.Change(m_pOriginal);
}
};
![Page 24: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/24.jpg)
싱글턴싱글턴, , 전역전역 변수변수TEST_FIXTURE(FChangeMode, TestNPC_SightRange)
{
GTestMgrWrapper<GNPCInfoMgr> m_NPCInfoMgrWrapper;
m_NPCInfo.nSightRange = 1000;
GNPC* pNPC = m_pMap->SpawnTestNPC(&m_NPCInfo);GNPC* pNPC = m_pMap->SpawnTestNPC(&m_NPCInfo);
CHECK_EQUAL(1000, pNPC->GetSightRange());
pNPC->ChangeMode(NPC_MODE_1);
CHECK_EQUAL(500, pNPC->GetSightRange());
}
![Page 25: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/25.jpg)
Refactoring Test CodeRefactoring Test Code• Mock Object
• override 키워드를 적극 활용• Google C++ Mocking Framework!
![Page 26: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/26.jpg)
Refactoring Test Code• UnitTestHelper
– 자주 사용하는 함수들은 Helper로따로 분리한다.
– 예) GUTHelper_NPC::SpawnNPC()– 예) GUTHelper_NPC::SpawnNPC()
![Page 27: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/27.jpg)
Refactoring Test Code• 자주 사용하는 Fixture는 체계적
으로 관리한다.class FBasePlayer;class FBasePlayer;
class FBaseItem;
class FBaseNPC;
class FBaseMap;
class FBaseNetClient;
![Page 28: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/28.jpg)
Refactoring Test CodeRefactoring Test Code• 자주 사용하는 Fixture는 체계적
으로 관리한다.class FForCombatTest : public FBaseMockLink,
public FBaseNetClient,
public FBasePlayer,
public FBaseMap,
public FBaseMapMgr,
public FBasePlayer
{
…
};
![Page 29: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/29.jpg)
Refactoring Test CodeRefactoring Test CodeClass Fduel // Fixture
{
…
void CHECK_DuelCancel()
{
CHECK_EQUAL(m_pLinkRequester->GetCommand(0).GetID(), MC_DUEL_CANCEL);
CHECK_EQUAL(m_pLinkTarget->GetCommand(0).GetID(), MC_DUEL_CANCEL);
}
void CHECK_DuelFinished(CPlayer* pWinner, CPlayer* pLoser)
{
MockLink* pWinnerLink = (pWinner==m_pPlayerRequester) ? M_pLinkRequester : m_pLinkT
const Mcommand& Command = pWinnerLink->GetCommand();
CHECK_EQUAL(Command.GetID(), MC_DUEL_FINISHED);
int nWinnerID, nLoserID;
Command.GetParam(&nWinnerID, 0, MPT_INT);
Command.GetParam(&nLoserID, 0, MPT_INT);
CHECK_EQUAL(nWinnerID, pWinner->GetID());
CHECK_EQUAL(nLoserID, pLoser->GetID());
}
}
![Page 30: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/30.jpg)
Refactoring Test CodeRefactoring Test CodeTEST_FIXTURE(FDuel, DuelQuestionRefuse)
{
CHECK_EQUAL(gmgr.pDuelMgr->GetCount(), 0);
DuelRequest();
CHECK_EQUAL(gmgr.pDuelMgr->GetCount(), 1);
BeginCommandRecord();BeginCommandRecord();
DuelResponse(false);
CHECK_DuelCancel();
}
![Page 31: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/31.jpg)
Database Unit TestDatabase Unit Test• 저장 프로시저, 트리거 등에 대한 유닛 테스트
• xDBUnit 프레임워크가 있지만 자체적으로작성했다.작성했다.– UnitTest++ 사용
![Page 32: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/32.jpg)
Database Unit TestDatabase Unit Test• 테스트 단계
1. SandBox에 데이터베이스, Table, SP 등 생성
2. 테스트에 필요한 데이터 집합(Seed Dataset)을 생성
3. 테스트 케이스 실행
4. 데이터 변경 검증
![Page 33: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/33.jpg)
Seed Seed DataSetDataSet
![Page 34: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/34.jpg)
Unit Test CodeUnit Test CodeDBTEST(FGuildDB, CreateGuild)
{
UTestDB.Seed(“GuildTestSeedData.xml”);
uint32 nMasterCID = DBTestHelper::GetCID(“Acc5Char1”);
uint32 nMem1 = DBTestHelper::GetCID(“Acc5Char2”);
uint32 nMem2 = DBTestHelper::GetCID(“Acc5Char3”);
CHECK((0 != nMasterCID) && (0 != nMem1) && (0 != nMem2));
// 길드 생성
TDBRecordSet rs1;
UTestDB.Execute(rs1, “{CALL spGuildCreate (‘%S’, %d)}”, “TGuild4”, nMasterCID);
int nGID = rs1.Field(“GID”).AsInt();
CHECK(0 != nGID);
// 길드가 추가되었는지 레코드 개수 확인
TDBRecordSet rs2;
UTestDB.Execute(rs2, “SELECT COUNT(*) AS cnt FROM dbo.Guild;”);
CHECK_EQUAL(1, rs2.GetFetchedCount());
CHECK_EQUAL(1, rs2.Field(“cnt”).AsInt());
}
![Page 35: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/35.jpg)
스크린샷스크린샷 테스트테스트• 특정 씬을 렌더링하여 원본 이미지와 같은
이미지인지 픽셀별로 비교하여 같은 픽셀
값인지 테스트
• 렌더링에 대한 UnitTest를 만들지 못하여• 렌더링에 대한 UnitTest를 만들지 못하여나온 대안
• 랜덤 요소 제거 등의 추가 작업이 필요함
![Page 36: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/36.jpg)
리플레이리플레이 테스트테스트• 벤치마크 테스트와 유사한 방식• 기능 테스트• 안정성 테스트성능 테스트• 성능 테스트
• 호환성 테스트
![Page 37: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/37.jpg)
리플레이리플레이 구현구현Command Queue
RecvPacketLocalEvent
SendPacket
Local복사복사복사복사
ReplayQueue
게임 루프ReplayQueue
복사복사복사복사
커맨드커맨드커맨드커맨드구조구조구조구조 ID Data
![Page 38: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/38.jpg)
Resource Resource ValidatorValidator• 기획자나 아티스트가 작업한 게임 데이터
(Assets)에 논리적으로 잘못된 값이 입력되었는지 검증
• 예시• 예시– 상점 인터랙션이 설정된 NPC는 비전투형인가?– 아이템 판매 가격이 구매 가격보다 높은가?– 몬스터에 설정된 스킬 애니메이션 파일이 존재하는가?– 맵의 포탈에 연결된 맵이 실재로 존재하는가?
![Page 39: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/39.jpg)
Resource Resource ValidatorValidator 구현구현• XML 파일은 일차적으로 Schema 검사• 검증 라이브러리를 따로 분리하여 각종 툴등에서 독립적으로 사용할 수 있도록 한다
• 예제• 예제
![Page 40: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/40.jpg)
Resource Resource ValidatorValidator
![Page 41: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/41.jpg)
Runtime Runtime ValidatorValidator• 서버 구동 중에 정적 분석으로 체크할 수없는 에러나 경고를 통지
• 종류– DB 쿼리 실패– DB 쿼리 실패– 성능 경고– AI 스크립트 오류– Assertion
![Page 42: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/42.jpg)
Runtime Runtime ValidatorValidator
![Page 43: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/43.jpg)
네트워크네트워크 테스트테스트• 클라이언트 패킷 핸들링을 XML로 쉽게 만들 수 있도록 한다.– 테스트 케이스
• 로그인 반복, 캐릭터 생성,삭제 반복, 이동 반복 등– 스케줄링
• 스트레스 테스트도 병행• Crash가 발생하거나 성능이 일정 수치 이하이면 테스트 실패로 간주
![Page 44: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/44.jpg)
AI AI 테스트테스트• 각종 몬스터를 랜덤으로 생성시켜 서로 전투시킴
• 테스트 실패– Crash가 발생– Crash가 발생– 성능이 일정 수치 이하
![Page 45: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/45.jpg)
Crash Dump ReporterCrash Dump Reporter
![Page 46: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/46.jpg)
Crash Dump AnalyzerCrash Dump Analyzer• 프로그램에 치명적인 오류가 있을 경우 덤프 파일을 서버에 전송
• 수집된 덤프 파일을 함수 별로 분류• 정기적으로 새로 올라온 덤프 파일 목록을• 정기적으로 새로 올라온 덤프 파일 목록을개발자들에게 메일로 보고
![Page 47: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/47.jpg)
Crash Dump Analyzer Crash Dump Analyzer 구현구현• 덤프 리포터• 심볼 서버 구축• WinDbg 의 Command-Line을 이용하여분석분석
• 최신 덤프 목록을 메일로 보내는 간단한툴 제작
![Page 48: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/48.jpg)
Crash Dump AnalyzerCrash Dump Analyzer
![Page 49: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/49.jpg)
Crash Dump AnalyzerCrash Dump Analyzer
![Page 50: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/50.jpg)
지속적인지속적인테스트를테스트를 유지하려면유지하려면??테스트를테스트를 유지하려면유지하려면??
![Page 51: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/51.jpg)
1. 1. 자동화자동화
2. 2. 테스트테스트 실패에실패에 대한대한 빠른빠른 대처대처2. 2. 테스트테스트 실패에실패에 대한대한 빠른빠른 대처대처
33. . 유지보수가유지보수가 가능하도록가능하도록 최대한최대한간단하게간단하게 제작제작
![Page 52: 생산적인 개발을 위한 지속적인 테스트](https://reader034.vdocuments.net/reader034/viewer/2022051017/55a8340f1a28abd4128b4664/html5/thumbnails/52.jpg)
감사합니다감사합니다