낡은 코드에단위테스트 넣기                         v 2.0                 박읷(NCsoft)   http://parkpd.egloos.com           twitter : rig...
강사 소개KGC 07, 게임에 적용해 보는 TDDKGC 08, NCDC 09 Lineage2 Production SystemKGC 09, NDC 10 사렺로 살펴보는 디버깅
1부 : 왜?2부 : 어떻게?3부 : 더 알아야 할 것들
1부 : 왜?2부 : 어떻게?3부 : 더 알아야 할 것들
낡은 코드?
낡은 코드• 단위 테스트가 없는• 리팩토링이 앆 되어 있는• 코드는 더럽게 많고, 실행은 되지만  조금만 고쳐도 여기저기에서 에러가 나서,  손 대기가 무서운• 남이 만듞• 또는 지금 내가 작성하고 있는 코드!
낡은 코드는 위대하다살아보니께 강핚 것이 오래가는 것이 아니라오래 가는 것이 강핚 거더라 - 짝패
코드는 변경되어야 핚다• 두 가지 방법  1. 땜빵  2. 제대로 고치기• 땜빵하는 이유  – 빨리 핛 수 있고  – 앆젂해 보여서  – 독박 쓰기 싫어서Actor::Attack(Actor& t){  // 다시는 이러지...
땜빵 코드의 악순홖코드가 보기어렵다
땜빵 코드의 악순홖코드가      기능추가 보기        하기어렵다       힘들다
땜빵 코드의 악순홖코드가      기능추가      if 문으로 보기        하기         대강어렵다       힘들다        고쳤다
땜빵 코드의 악순홖코드가      기능추가      if 문으로 보기        하기         대강어렵다       힘들다        고쳤다                    버그                 ...
땜빵 코드의 악순홖코드가      기능추가      if 문으로 보기        하기         대강어렵다       힘들다        고쳤다                    버그         야근이다    ...
땜빵 코드의 악순홖코드가      기능추가      if 문으로 보기        하기         대강어렵다       힘들다        고쳤다                    버그         야근이다    ...
땜빵 코드의 악순홖코드가       기능추가      if 문으로 보기         하기         대강어렵다        힘들다        고쳤다닭집이나                 버그          야근이...
리팩토링이 필요하다
낡은 코드의 위험성• 리팩토링을 해야 하는데…• 제대로 고치자니 무섭고• 어떻게 하면  – 제대로 고쳤다는 걸  – 이젂에 잘 돌아가는 것을 고장내지 않았다고• 확싞핛 수 있을까?
단위테스트가 필요하다
팀장님이 단위테스트를 싫어해요• 더 많이 코딩해야 핚다• 초기에는 오히려 버그가 더 많이 나온다• 항상 시갂이 부족하다
그럼에도 불구하고
그럼에도 불구하고
에러 개수 변동                                                                     KGC 08 박일 - Lineage2 Production systemCh5   I...
MS, IBM 역시 시갂은 더 걸렸지만               Time taken to code a feature140%                        135%             120%         ...
버그 갯수는 현저히 죿었다                    Using Test Driven Design140%120%100%80%              61%60%                             ...
NHN 단위 테스트 도입 사렺꾸준히 자라나는 소프트웨어(Software that grows!) 만들기 - 박종빈
설득의 4단계          1. 미리 해 보기         2. 위험 무릅쓰기    3.   테스트 도우미 되기           4. 공권력 도입
1 단계 : 미리 해 보기• 스스로 확싞이 없다면 남을 설득하기 어렵다• 핚 번 실망핚 사람들을 다시 설득하기란 어렵다• 갂단핚 Toy 프로젝트로 연습해 본다
2 단계 : 위험 무릅쓰기• 테스트의 중요성을 공유핚다• 팀장을 설득핚다 – 제가 핚 번 해 보고 싶습니다• 팀을 앆심시킨다 – #ifdef DEBUG & USE_TDD 같은 macro 로 격리 – Release 빌드에...
3 단계 : 테스트 도우미 되기• 테스트 에러 -> 젂체 이메읷 -> 확읶• 다른 사람 작업 붂야에 테스트 붙이기 – 테스트가 좋다는 점을 느낄 수 있게 핚다
4 단계 : 공권력 도입• 꽤 많이 확산되었다• 싞입부터 공략• 그래도 테스트를 작성  하지 않는다?  앆 하면 짤랐다는  사람도 있었다       AAA Automated Testing for AAA Games    ...
1부 ‘왜’ 요약• 낡은 코드• 왜 단위테스트를 해야 하는가?• 설득의 4단계 – 미리 해 보기 – 위험 무릅쓰기 – 테스트 도우미 되기 – 공권력 도입
20% 버그 감소 vs 20% 개발기갂 증가
20% 버그 감소 vs 20% 개발기갂 증가
1부 : 왜?2부 : 어떻게?3부 : 더 알아야 할 것들
프로그래머라면코드를 보자
검색어 : 박피디 혹은 박일
검색어 : 박규리 생얼
Hello World 테스트#include <gtest/gtest.h>TEST(FixtureBase, TestTest) {  EXPECT_TRUE(true); // 무조건 성공}int _tmain(int argc, _T...
Hello World 테스트#include <gtest/gtest.h>TEST(TestTest, TestTest) {  EXPECT_TRUE(true); // 무조건 성공}int _tmain(int argc, _TCHA...
class TersePrinter : public EmptyTestEventListener {   void OnTestPartResult(const TestPartResult& r) {        char const*...
class TersePrinter : public EmptyTestEventListener {   void OnTestPartResult(const TestPartResult& r) {          char cons...
class TersePrinter : public EmptyTestEventListener {   void OnTestPartResult(const TestPartResult& r) {        char const*...
Fixture  경기, 붙박이 세갂  테스트 개발 홖경
Fixture 실행순서TEST(FixtureBase, AllocInt) {  EXPECT(1, *m_pData);}
Fixture 실행순서struct FixtureBase : public testing::Test {   virtual void SetUp() {       m_pData = new int(1);   }   virtual...
Fixture 실행순서struct FixtureBase : public testing::Test {   virtual void SetUp() {            테스트에 필요핚       m_pData = new i...
Fixture 실행순서struct FixtureBase : public testing::Test {   virtual void SetUp() {       m_pData = new int(1);   }   virtual...
Fixture 실행순서struct FixtureBase : public testing::Test {   virtual void SetUp() {       m_pData = new int(1);   }   virtual...
초간단 LegacyMMORPG
무엇을 테스트 핛 것읶가?• Bug Report 와 기획서 홗용• 예 : Test Plan – 공격을 하면 10% 확률로 크리티컬이 터집니다.   크리티컬이 터지면 2배의 피해를 입힐 수 있습니다.
이상적읶 테스트 코드TEST(FixtureActor2, 크리티컬공격) {  // 평타  double d1 = actor1.Attack(actor2);  // 크리티컬 공격  double d2 = actor1.Attack...
테스트 코드의 문제점 #1TEST(FixtureActor2, 크리티컬공격) {  // 평타  double d1 = actor1.Attack(actor2);  // 크리티컬 공격  double d2 = actor1.Att...
1단계 : 랜덤값 제어
Random값 제어static bool g_UseRand = false;     struct FixtureBase : public Test {static double g_RandValue = 0.0;      // 테스...
변경된 테스트 코드TEST(FixtureActor2, 크리티컬공격) {  // 평타    SetRandValue(0.0);    double d1 = actor1.Attack(actor2);    // 크리티컬 공격  ...
테스트 코드의 문제점 #2TEST(FixtureActor2, AttackCritical) {   // 평타   SetRandValue(0.0);   double d1 = actor1.Attack(actor2);   //...
1단계 : 랜덤값 제어2단계 : Pc 객체 없이 테스트
기졲 Actor::Attackdouble Actor::Attack(Actor& t) {  if (IsDead() || t.IsDead())       return 0.0;    int lvDif = min(Lev() –...
코드 쪼개기double Actor::Attack(Actor& t) {   TEST(FixtureBase, Critical) {   if (IsDead() || t.IsDead())        // 5% 확률      ...
코드 쪼개기double Actor::Attack(Actor& t) {   TEST(FixtureBase, Critical) {   if (IsDead() || t.IsDead())        // 5% 확률      ...
더 잘게 쪼개기double Actor::Attack(Actor& t) {               TEST_F(FixtureBase, CanAttack) {    if (CanAttack(IsDead(), t.IsDea...
더 잘게 쪼개기double Actor::Attack(Actor& t) {             TEST_F(FixtureBase, CanAttack) {    if (CanAttack(IsDead(), t.IsDead(...
1단계 : 랜덤값 제어2단계 : Pc 객체 없이 테스트3단계 : Pc 객체 생성
최상단 클래스부터 핚걸음씩TEST_F(FixtureBase, Obj) {  Obj* o = new Obj();  EXPECT_TRUE(o != NULL);  EXPECT_EQ(1, o->m_Ref);}
익명 생성 메소드struct FixturePc2 : public FixtureBase {   Pc* CreatePc() {        static int pcNum = 0;        ++pcNum;        s...
Dummy Socketstruct FixturePc2 : public FixtureBase {   Pc* CreatePc() {       return new Pc(new SocketDummy());     }   …}...
1단계 : 랜덤값 제어2단계 : Pc 객체 없이 테스트3단계 : Pc 객체 생성4단계 : 테스트 결과 짂단
Mock Socketstruct FixtureActor2 : public FixtureBase {   Pc* CreatePc(){        return new Pc(new SocketMock());}};class S...
SystemMessage, LogShowedSystemMsg(MSG_ID(10));Log.AddedLog(LOG_ID(3047));
짂단용 코드class Actor {                               행위테스트#ifdef USE_TDD   struct TestData {int m_SkillLaunched;}; 결과 기록용   T...
갂단핚 Mock 만들기class Pc {protected:   int m_Test;   virtual void Test() {}};struct PcMock : public Pc {   using Pc::m_Test; /...
아니? 은닉화는?• private, protected 를 해야 앆젂하지 않나?  – 테스트 없는 private 보다,    테스트가 있는 public 이 훨씬 앆젂하다• 그래도 정문 테스트가 우선이다• 실제로 호출되는 ...
ActorMockdouble ActorMock::OnDamaged(double dmg) {  m_DamageSum += dmg; // 짂단용 데이터  return Actor::OnDamange(dmg);}
Google mockclass MockTurtle : public Turtle{       using testing::AtLeast;   MOCK_METHOD0(                        using te...
GetPcState인기는 쉽게, 쓰기는 어렵게struct PcState {   double m_WeaponBonus;   double m_MagicBonus;   ...};TEST(FixturePc1WithSkill, ...
1단계   :   랜덤값 제어2단계   :   Pc 객체 없이 테스트3단계   :   Pc 객체 생성4단계   :   테스트 결과 짂단5단계 : 공성
struct Fixture공성준비 : public FixtureBase {   virtual void SetUp() {        m_PcMaker.SetPcCount(30); // 30명 생성        혈맹1 =...
Fixture 로 공성 테스트하기FixtureBaseFixture공성기초Fixture공성죾비Fixture공성시작됨Fixture공성종료직젂
Fixture 로 공성 테스트하기FixtureBaseFixture공성기초     Pc가 점령핚 성읷때?                Npc가 점령핚 성읷때?                죾비과정에서 취소하면?Fixture공...
1단계   :   랜덤값 제어2단계   :   Pc 객체 없이 테스트3단계   :   Pc 객체 생성4단계   :   테스트 결과 짂단5단계   :   공성6단계 : 의졲 제거
파티 초대
패킷 통싞 의졲성 제거
packet handler 를 wrappingTEST(FixtureActor2, Party) {  actor1.OnPacketPartyInvite(actor2);  EXPECT(actor1.SentProtocol(S_P...
테스트 코드 리팩토링Party* Party::PartyMake(Actor& master, Actor& guest) {  master.OnPacketPartyInvite(guest);  guest.OnPacketParty...
젂역변수 대싞 singletonWorld& World::Inst() {  #ifdef USE_TDD  if (g_InTest) {       return g_TestWorld;  }  #endif  return g_Wo...
singleton 대싞 읶자로 받기struct FixtureActor2 : public FixtureBase {   virtual void SetUp() {       actorInRealWorld = new Pc(g_...
시갂 의졲 제거 – Buff 테스트TEST(FixtureActor2WithSkill, DOT) {  actor1.UseSkill(actor2 , skill1);  EXPECT(1 == actor1.GetDotCount(...
DB 의졲 제거bool DB::Init() {   #ifdef USE_TDD   if (g_InTest) {     // do nothing     return true;   }   #endif   // do real ...
1단계   :   랜덤값 제어2단계   :   Pc 객체 없이 테스트3단계   :   Pc 객체 생성4단계   :   테스트 결과 짂단5단계   :   공성6단계   :   의졲 제거7단계 : 보너스
Performance 검사시갂이 가장 오래 걸리는 테스트는?FixtureBase::~FixtureBase() {  g_TestPerfMap[testName] = sec;}
Memory Leak Detector 1struct Item {       Item() { g_ItemCount++; }       ~Item() { g_ItemCount--; }};struct FixtureBase {...
Memory Leak Detector 2#include <crtdbg.h>int AllocHook(int nAllocType, size_t nSize, ... {       switch (nAllocType) {    ...
2부 ‘어떻게’ 요약• 테스트 설치, Fixture 소개• 테스트 코드 도입 단계  1.       랜덤값 제어  2.       객체 없이 테스트       •     메서드 잘게 쪼개기  3.       Pc 객체 ...
1. 왜?2. 어떻게?3. 더 알아야 할 것들
다양핚 테스트          특성   테스트          회귀   테스트          학습   테스트      이미지 비교   테스트       리플레이    테스트          퍼징   테스트
다양핚 테스트무엇을       특성 테스트       회귀 테스트       학습 테스트      이미지 비교 테스트       리플레이 테스트          퍼징 테스트
특성 테스트변경하려는 클래스를 위해 가장 먼저 만드는읷종의 회귀 테스트 현재 상태의 특성을 기록 “What Should Do” 가 아닊 “What Really Do” 상태를 보졲핚다.
Attack 함수 특성 테스트double Actor::Attack(Actor& t) {  int levDiff = Level() – t.Level();  return CalcDamage(levDiff , WBonus()...
Attack 함수 특성 테스트double Actor::Attack(Actor& t) {  int levDiff = Level() – t.Level();  return CalcDamage(levDiff , WBonus()...
TEST(FixtureAttack, CalcDamage) {  struct TestData {       int LevDif;       double WBonus, Expect;  };    Data data[] = {...
회귀테스트(Regression Test)?잘 되던게 왜 갑자기 앆 되는거야?회귀(Regression) : 퇴행, 퇴보 되던 기능이 업데이트 이후로 앆 되는 현상회귀테스트 : 회귀가 발생했는지를 검사하는 테스트
회귀테스트(Regression Test)?잘 되던게 왜 갑자기 앆 되는거야?
회귀테스트??• 2년 젂에 젂투 관렦 서버 코드가  어떻게 돌아가는지 보고 싶다면 – 2년 젂 Server 소스 snapshot 받아서 빌드 – 같은 날의 Client 소스 snapshot 받아서 빌드 – 같은 날의 게...
회귀테스트 with 단위테스트!!• 2년 젂에 젂투 관렦 서버 코드가  어떻게 돌아가는지 보고 싶다면 – 2년 젂 Server 소스 snapshot 받아서 빌드 – 같은 날의 Client 소스 snapshot 받아서 빌...
CI(지속적읶 통합)와 연결• CruiseControl.Net 에서 UnitTest 의  성공/실패 결과를 통보• 개발 중에는 금방 끝나는 테스트만 실행하고  오래 걸리는 회귀 테스트는 CI 에서만 실행
학습테스트• 잘 모르는 코드를 연구 – 예 : hashtable• 결과 – 실행되는 문서가 생긴다 – 관렦 코드에서 회귀가 생기는 것을 방지
다양핚 테스트들어떻게          특성 테스트          회귀 테스트          학습 테스트      이미지 비교 테스트       리플레이 테스트          퍼징 테스트
이미지 비교 테스트KGC 09 생산적읶 개발을 위핚 지속적읶 테스트 - 남기룡
리플레이 테스트
퍼징 테스트(Fuzzing Test)•   Monkey Test•   해커는 뭐듞지 핛 수 있다•   비정상적읶 패킷 보내기•   예외적읶 곳에 로그 남기기    – 호출되는 순갂 CRASHif (0 == itemOwn...
단위테스트FAQ
서버 vs 클라이언트• MVC 패턴에서 서버는 M, 클라이언트는 VC – 클라이언트에서 키 입력 보내면(C), 서버에서 판단   해서(M), 클라이언트에서 결과를 랜더링(V) 핚다.
서버 vs 클라이언트• MVC 패턴에서 서버는 M, 클라이언트는 VC – 클라이언트에서 키 입력을 보내면(C), 서버에서 판   단해서(M), 클라이언트에서 결과를 랜더링(V) 핚다.
테스트가 가끔 실패해요• 지속 공유 픽스처 (xUnit)• goolgle test –gtest_repeat : 몇 번 반복해서 –gtest_filter : 원하는 테스트만 –gtest_shuffle : 순서를 섞어서• ...
Multi-Thread• 테스트는 Single-Thread 로 실행 – Multi-Thread 로 실행되는 로직부붂만 따로 실행• 메시지는 바로 callback 호출
DB 테스트 - Fixture transactionclass FixtureDb : Test {         TEST(FixtureDb, InsertTest) {   void SetUp() {               ...
3부 ‘더 알아야 핛 것들’ 요약• 다양핚 테스트들 –   특성 테스트 –   회귀 테스트 –   학습 테스트 –   이미지 비교 테스트 –   리플레이 테스트 –   퍼징 테스트• 단위테스트 FAQ – 서버와 클라이언...
마지막으로알아야 할 것
단위테스트는 또 다른 테스트읷 뿐 개발팀   단위테스트       개발팀 QA            QA팀                테스트서버                   알파테스트
감사합니다 Q&A
References•   TDD, UnitTest for games in KGC 2007•   Lineage2 Production system in KGC 2008•   온라읶 게임에서 사렺로 살펴보는 디버깅 in KG...
이미지•   짝패     –   http://kr.blog.yahoo.com/joun8661/archive/2006/12?m=lc•   it’s not my job     –   http://www.joe-ks.com/...
Books
KGC2010 박일 낡은 코드와 단위테스트
Upcoming SlideShare
Loading in …5
×

KGC2010 박일 낡은 코드와 단위테스트

1,733 views

Published on

1 Comment
6 Likes
Statistics
Notes
No Downloads
Views
Total views
1,733
On SlideShare
0
From Embeds
0
Number of Embeds
10
Actions
Shares
0
Downloads
0
Comments
1
Likes
6
Embeds 0
No embeds

No notes for slide

KGC2010 박일 낡은 코드와 단위테스트

  1. 1. 낡은 코드에단위테스트 넣기 v 2.0 박읷(NCsoft) http://parkpd.egloos.com twitter : rigmania
  2. 2. 강사 소개KGC 07, 게임에 적용해 보는 TDDKGC 08, NCDC 09 Lineage2 Production SystemKGC 09, NDC 10 사렺로 살펴보는 디버깅
  3. 3. 1부 : 왜?2부 : 어떻게?3부 : 더 알아야 할 것들
  4. 4. 1부 : 왜?2부 : 어떻게?3부 : 더 알아야 할 것들
  5. 5. 낡은 코드?
  6. 6. 낡은 코드• 단위 테스트가 없는• 리팩토링이 앆 되어 있는• 코드는 더럽게 많고, 실행은 되지만 조금만 고쳐도 여기저기에서 에러가 나서, 손 대기가 무서운• 남이 만듞• 또는 지금 내가 작성하고 있는 코드!
  7. 7. 낡은 코드는 위대하다살아보니께 강핚 것이 오래가는 것이 아니라오래 가는 것이 강핚 거더라 - 짝패
  8. 8. 코드는 변경되어야 핚다• 두 가지 방법 1. 땜빵 2. 제대로 고치기• 땜빵하는 이유 – 빨리 핛 수 있고 – 앆젂해 보여서 – 독박 쓰기 싫어서Actor::Attack(Actor& t){ // 다시는 이러지 말자 if (t.Class == 104420) return 0.0; // 기존코드… its not my job
  9. 9. 땜빵 코드의 악순홖코드가 보기어렵다
  10. 10. 땜빵 코드의 악순홖코드가 기능추가 보기 하기어렵다 힘들다
  11. 11. 땜빵 코드의 악순홖코드가 기능추가 if 문으로 보기 하기 대강어렵다 힘들다 고쳤다
  12. 12. 땜빵 코드의 악순홖코드가 기능추가 if 문으로 보기 하기 대강어렵다 힘들다 고쳤다 버그 생겼다
  13. 13. 땜빵 코드의 악순홖코드가 기능추가 if 문으로 보기 하기 대강어렵다 힘들다 고쳤다 버그 야근이다 생겼다
  14. 14. 땜빵 코드의 악순홖코드가 기능추가 if 문으로 보기 하기 대강어렵다 힘들다 고쳤다 버그 야근이다 생겼다
  15. 15. 땜빵 코드의 악순홖코드가 기능추가 if 문으로 보기 하기 대강어렵다 힘들다 고쳤다닭집이나 버그 야근이다차리자 생겼다
  16. 16. 리팩토링이 필요하다
  17. 17. 낡은 코드의 위험성• 리팩토링을 해야 하는데…• 제대로 고치자니 무섭고• 어떻게 하면 – 제대로 고쳤다는 걸 – 이젂에 잘 돌아가는 것을 고장내지 않았다고• 확싞핛 수 있을까?
  18. 18. 단위테스트가 필요하다
  19. 19. 팀장님이 단위테스트를 싫어해요• 더 많이 코딩해야 핚다• 초기에는 오히려 버그가 더 많이 나온다• 항상 시갂이 부족하다
  20. 20. 그럼에도 불구하고
  21. 21. 그럼에도 불구하고
  22. 22. 에러 개수 변동 KGC 08 박일 - Lineage2 Production systemCh5 Interlude CT1 CT1.5 CT2-1 CT2-2 CT2-3 팀error갯수 내error갯수 에러 Fix 시갂 평균 Ch5 Interlude CT1 CT1.5 CT2-1 CT2-2 CT2-3 팀FixAvg 내FixAvg
  23. 23. MS, IBM 역시 시갂은 더 걸렸지만 Time taken to code a feature140% 135% 120% 125%120% 115%100%80%60%40%20% 0% IBM: Drivers MS: Windows MS: MSN MS: VS WithoutTDD Using TDD
  24. 24. 버그 갯수는 현저히 죿었다 Using Test Driven Design140%120%100%80% 61%60% 38%40% 24%20% 9% 0% IBM: Drivers MS: Windows MS: MSN MS: VS Time To Code Feature Defect density of team
  25. 25. NHN 단위 테스트 도입 사렺꾸준히 자라나는 소프트웨어(Software that grows!) 만들기 - 박종빈
  26. 26. 설득의 4단계 1. 미리 해 보기 2. 위험 무릅쓰기 3. 테스트 도우미 되기 4. 공권력 도입
  27. 27. 1 단계 : 미리 해 보기• 스스로 확싞이 없다면 남을 설득하기 어렵다• 핚 번 실망핚 사람들을 다시 설득하기란 어렵다• 갂단핚 Toy 프로젝트로 연습해 본다
  28. 28. 2 단계 : 위험 무릅쓰기• 테스트의 중요성을 공유핚다• 팀장을 설득핚다 – 제가 핚 번 해 보고 싶습니다• 팀을 앆심시킨다 – #ifdef DEBUG & USE_TDD 같은 macro 로 격리 – Release 빌드에서는 file 에서 오른쪽 버튺 -> general 탭 에서 exclude file from build
  29. 29. 3 단계 : 테스트 도우미 되기• 테스트 에러 -> 젂체 이메읷 -> 확읶• 다른 사람 작업 붂야에 테스트 붙이기 – 테스트가 좋다는 점을 느낄 수 있게 핚다
  30. 30. 4 단계 : 공권력 도입• 꽤 많이 확산되었다• 싞입부터 공략• 그래도 테스트를 작성 하지 않는다? 앆 하면 짤랐다는 사람도 있었다 AAA Automated Testing for AAA Games Francesco Carucci (Crytek) GDC09 Europe• 적어도 테스트 에러는 잡을 것!
  31. 31. 1부 ‘왜’ 요약• 낡은 코드• 왜 단위테스트를 해야 하는가?• 설득의 4단계 – 미리 해 보기 – 위험 무릅쓰기 – 테스트 도우미 되기 – 공권력 도입
  32. 32. 20% 버그 감소 vs 20% 개발기갂 증가
  33. 33. 20% 버그 감소 vs 20% 개발기갂 증가
  34. 34. 1부 : 왜?2부 : 어떻게?3부 : 더 알아야 할 것들
  35. 35. 프로그래머라면코드를 보자
  36. 36. 검색어 : 박피디 혹은 박일
  37. 37. 검색어 : 박규리 생얼
  38. 38. Hello World 테스트#include <gtest/gtest.h>TEST(FixtureBase, TestTest) { EXPECT_TRUE(true); // 무조건 성공}int _tmain(int argc, _TCHAR* argv[]) { testing::InitGoogleTest(&argc, argv); RUN_ALL_TESTS();}
  39. 39. Hello World 테스트#include <gtest/gtest.h>TEST(TestTest, TestTest) { EXPECT_TRUE(true); // 무조건 성공}int _tmain(int argc, _TCHAR* argv[]) { testing::InitGoogleTest(&argc, argv); RUN_ALL_TESTS();}
  40. 40. class TersePrinter : public EmptyTestEventListener { void OnTestPartResult(const TestPartResult& r) { char const* const f = "%s(%d): error: (%s)n"; sprintf_s(buf, f, r.file_name(), r.line_number(), r.summary()); OutputDebugString(buffer); }};int _tmain(int argc, _TCHAR* argv[]) { testing::InitGoogleTest(&argc, argv); UnitTest& unit_test = *UnitTest::GetInstance(); TestEventListeners& listeners = unit_test.listeners(); delete listeners.Release(listeners.default_result_printer()); listeners.Append(new TersePrinter); unit_test.Run(); if (unit_test.Failed()) { __debugbreak(); // 테스트가 실패하면 중지 }
  41. 41. class TersePrinter : public EmptyTestEventListener { void OnTestPartResult(const TestPartResult& r) { char const* const f = "%s(%d): error: (%s)n"; sprintf_s(buf, f, r.file_name(), r.line_number(), r.summary()); OutputDebugString(buffer); }};int _tmain(int argc, _TCHAR* argv[]) { testing::InitGoogleTest(&argc, argv); UnitTest& unit_test = *UnitTest::GetInstance(); TestEventListeners& listeners = unit_test.listeners(); delete listeners.Release(listeners.default_result_printer()); listeners.Append(new TersePrinter); unit_test.Run(); if (unit_test.Failed()) { __debugbreak(); // 테스트가 실패하면 중지 }
  42. 42. class TersePrinter : public EmptyTestEventListener { void OnTestPartResult(const TestPartResult& r) { char const* const f = "%s(%d): error: (%s)n"; sprintf_s(buf, f, r.file_name(), r.line_number(), r.summary()); OutputDebugString(buffer); }};int _tmain(int argc, _TCHAR* argv[]) { testing::InitGoogleTest(&argc, argv); --gtest_break_on_failure 인자 UnitTest& unit_test = *UnitTest::GetInstance(); TestEventListeners& listeners = unit_test.listeners(); delete listeners.Release(listeners.default_result_printer()); listeners.Append(new TersePrinter); unit_test.Run(); if (unit_test.Failed()) { __debugbreak(); // 테스트가 실패하면 중지 }
  43. 43. Fixture 경기, 붙박이 세갂 테스트 개발 홖경
  44. 44. Fixture 실행순서TEST(FixtureBase, AllocInt) { EXPECT(1, *m_pData);}
  45. 45. Fixture 실행순서struct FixtureBase : public testing::Test { virtual void SetUp() { m_pData = new int(1); } virtual void TearDown() { delete m_pData; } int* m_pData;};TEST(FixtureBase, AllocInt) { EXPECT(1, *m_pData);}
  46. 46. Fixture 실행순서struct FixtureBase : public testing::Test { virtual void SetUp() { 테스트에 필요핚 m_pData = new int(1); 홖경을 설치핚다 } virtual void TearDown() { delete m_pData; } int* m_pData;};TEST(FixtureBase, AllocInt) { EXPECT(1, *m_pData);}
  47. 47. Fixture 실행순서struct FixtureBase : public testing::Test { virtual void SetUp() { m_pData = new int(1); } virtual void TearDown() { delete m_pData; } int* m_pData;};TEST(FixtureBase, AllocInt) { int* m_pData 를 EXPECT(1, *m_pData); FixtureBase 멤버변수로 만들면} 테스트에서 사용핛 수 있다
  48. 48. Fixture 실행순서struct FixtureBase : public testing::Test { virtual void SetUp() { m_pData = new int(1); } virtual void TearDown() { 테스트하느라 설치했던 delete m_pData; 홖경을 정리핚다 } int* m_pData;};TEST(FixtureBase, AllocInt) { EXPECT(1, *m_pData);}
  49. 49. 초간단 LegacyMMORPG
  50. 50. 무엇을 테스트 핛 것읶가?• Bug Report 와 기획서 홗용• 예 : Test Plan – 공격을 하면 10% 확률로 크리티컬이 터집니다. 크리티컬이 터지면 2배의 피해를 입힐 수 있습니다.
  51. 51. 이상적읶 테스트 코드TEST(FixtureActor2, 크리티컬공격) { // 평타 double d1 = actor1.Attack(actor2); // 크리티컬 공격 double d2 = actor1.Attack(actor2); EXPECT(d1 * 2.0 == d2);}
  52. 52. 테스트 코드의 문제점 #1TEST(FixtureActor2, 크리티컬공격) { // 평타 double d1 = actor1.Attack(actor2); // 크리티컬 공격 double d2 = actor1.Attack(actor2); EXPECT(d1 * 2.0 == d2);} random 값 제어는 어떻게? 공격을 하면 10% 확률로 크리티컬이 터집니다. 크리티컬이 터지면 2배의 피해를 입힐 수 있습니다.
  53. 53. 1단계 : 랜덤값 제어
  54. 54. Random값 제어static bool g_UseRand = false; struct FixtureBase : public Test {static double g_RandValue = 0.0; // 테스트 시작 전에 정리한다double GetRand() { virtual void SetUp() { #ifdef USE_TDD g_UseRandV = false; if (g_InTest && g_UseRand) { g_RandValue = 0.0; return g_RandValue; } } } #endif return 기존 rand 계산값; TEST(FixtureBase, 랜덤테스트) {} SetRandValue(1.0);void SetRandValue(double v) { EXPECT(1.0, GetRand()); g_UseRand = true; } g_RandValue = v;}
  55. 55. 변경된 테스트 코드TEST(FixtureActor2, 크리티컬공격) { // 평타 SetRandValue(0.0); double d1 = actor1.Attack(actor2); // 크리티컬 공격 SetRandValue(100.0); double d2 = actor1.Attack(actor2); EXPECT(d1 * 2.0 == d2);}
  56. 56. 테스트 코드의 문제점 #2TEST(FixtureActor2, AttackCritical) { // 평타 SetRandValue(0.0); double d1 = actor1.Attack(actor2); // 크리티컬 공격 SetRandValue(100.0); double d2 = actor1.Attack(.actor2); EXPECT(d1 * 2.0 == d2);} actor 를 생성핛 수 있는가?
  57. 57. 1단계 : 랜덤값 제어2단계 : Pc 객체 없이 테스트
  58. 58. 기졲 Actor::Attackdouble Actor::Attack(Actor& t) { if (IsDead() || t.IsDead()) return 0.0; int lvDif = min(Lev() – t.Lev(), 1); double criticalBonus = 1.0; // 10% if (GetRand() < 0.1) criticalBonus = 2.0; return lvDif * WeaponBonus() * criticalBonus;}
  59. 59. 코드 쪼개기double Actor::Attack(Actor& t) { TEST(FixtureBase, Critical) { if (IsDead() || t.IsDead()) // 5% 확률 return 0.0; SetRandValue(0.05); int lvDif = min(Lev() – EXPECT( t.Lev(), 1); return 2.0, Actor::CriticalBonus()); lvDif * WeaponBonus() * CriticalBonus();} // 11% 확률// static 함수 SetRandValue(0.11);double Actor::CriticalBonus() { EXPECT( if (GetRand() < 0.1) 1.0, return 2.0; Actor::CriticalBonus()); return 1.0; }} 공격을 하면 10% 확률로 크리티컬이 터집니다. 크리티컬이 터지면 2배의 피해를 입힐 수 있습니다.
  60. 60. 코드 쪼개기double Actor::Attack(Actor& t) { TEST(FixtureBase, Critical) { if (IsDead() || t.IsDead()) // 5% 확률 return 0.0; SetRandValue(0.05); int lvDif = min(Lev() – EXPECT( t.Lev(), 1); return 2.0, Attack Attack Actor::CriticalBonus()); lvDif * WeaponBonus() * CriticalBonus();} // 11% 확률// static 함수 SetRandValue(0.11);double Actor::CriticalBonus() { if (GetRand() < 0.1) CriticalBonus EXPECT( 1.0, return 2.0; Actor::CriticalBonus()); return 1.0; }} 공격을 하면 10% 확률로 크리티컬이 터집니다. 크리티컬이 터지면 2배의 피해를 입힐 수 있습니다.
  61. 61. 더 잘게 쪼개기double Actor::Attack(Actor& t) { TEST_F(FixtureBase, CanAttack) { if (CanAttack(IsDead(), t.IsDead()) EXPECT_TRUE( return 0.0; Actor::CanAttack(true, true); return CalcDamage( EXPECT_FALSE( Level() – t.Level(), Actor::CanAttack(false, true); WeaponBonus(), EXPECT_FALSE ( CriticalBonus()); Actor::CanAttack(false, false);} }bool Actor::CanAttack( bool imDead, bool targetDead) { TEST_F(FixtureBase, CalcDamage) { return imDead && targetDead; EXPECT_EQ(} 1, Actor::CalcDamage(1, 1, 1));double Actor::CalcDamage(int levDiff, double EXPECT_EQ( weaponBonus, double criticalBonus) { 2, Actor::CalcDamage(1, 1, 2)); return (1 + min(levDiff, 0)) * } weaponBonus * criticalBonus;} 공격을 하면 10% 확률로 크리티컬이 터집니다. 크리티컬이 터지면 2배의 피해를 입힐 수 있습니다.
  62. 62. 더 잘게 쪼개기double Actor::Attack(Actor& t) { TEST_F(FixtureBase, CanAttack) { if (CanAttack(IsDead(), t.IsDead()) return 0.0; CanAttack EXPECT_TRUE( Actor::CanAttack(true, true); return CalcDamage( EXPECT_FALSE( Level() – t.Level(), WeaponBonus(), CriticalBonus()); CalcDamage Actor::CanAttack(false, true); EXPECT_FALSE ( Actor::CanAttack(false, false); Attack Attack} }bool Actor::CanAttack( bool imDead, bool targetDead) { TEST_F(FixtureBase, CalcDamage) { return imDead && targetDead; EXPECT_EQ(} 1, Actor::CalcDamage(1, 1, 1)); CriticalBonus CriticalBonusdouble Actor::CalcDamage(int lvDif, double EXPECT_EQ( weaponBonus, double criticalBonus) { 2, Actor::CalcDamage(1, 1, 2)); return (1 + min(levDiff, 0)) * } weaponBonus * criticalBonus;} 공격을 하면 10% 확률로 크리티컬이 터집니다. 크리티컬이 터지면 2배의 피해를 입힐 수 있습니다.
  63. 63. 1단계 : 랜덤값 제어2단계 : Pc 객체 없이 테스트3단계 : Pc 객체 생성
  64. 64. 최상단 클래스부터 핚걸음씩TEST_F(FixtureBase, Obj) { Obj* o = new Obj(); EXPECT_TRUE(o != NULL); EXPECT_EQ(1, o->m_Ref);}
  65. 65. 익명 생성 메소드struct FixturePc2 : public FixtureBase { Pc* CreatePc() { static int pcNum = 0; ++pcNum; sprintf_s(name, “testpc_%d”, pcNum); return new Pc(name); } virtual void SetUp() { actor1 = CreatePc(); actor2 = CreatePc(); }} testpc_1 testpc_2 testpc_3 testpc_4 testpc_5
  66. 66. Dummy Socketstruct FixturePc2 : public FixtureBase { Pc* CreatePc() { return new Pc(new SocketDummy()); } …};class SocketDummy : public Socket { bool Send(int protocol, byte* buf) { // do nothing return true; }}
  67. 67. 1단계 : 랜덤값 제어2단계 : Pc 객체 없이 테스트3단계 : Pc 객체 생성4단계 : 테스트 결과 짂단
  68. 68. Mock Socketstruct FixtureActor2 : public FixtureBase { Pc* CreatePc(){ return new Pc(new SocketMock());}};class SocketMock : public Socket { virtual bool Send(int protocol, byte* buf) { m_Protocol.push_back(protocol); } bool SentProtocol(int protocol) { return find(m_Protocol.begin(), m_Protocol.end(), protocol); }};TEST(FixtureActor2, AttackPacket) { EXPECT(10.0, actor1.Attack(actor2)); EXPECT(actor1.SentProtocol(S_Attacking)); actor1->Dead(); EXPECT(0.0, actor2.Attack(actor1)); EXPECT(false == actor2.SentProtocol(S_Attacking));
  69. 69. SystemMessage, LogShowedSystemMsg(MSG_ID(10));Log.AddedLog(LOG_ID(3047));
  70. 70. 짂단용 코드class Actor { 행위테스트#ifdef USE_TDD struct TestData {int m_SkillLaunched;}; 결과 기록용 TestData m_TestData; 데이터 구조체#endif};class FakeSkill : public Skill { 스크립트에 void Launched(Actor& a, Actor& t) { 의졲하지 말고 a.m_TestData.m_SkillLaunched++; } 하드코딩핚다};TEST(FixtureActor2WithSkill, UseSkill) { actor1.UseSkill(actor2, skill1); EXPECT(1 == actor1.m_TestData.m_SkillLaunched);}
  71. 71. 갂단핚 Mock 만들기class Pc {protected: int m_Test; virtual void Test() {}};struct PcMock : public Pc { using Pc::m_Test; // 부모 클래스의 멤버를 public 으로 using Pc::Test;};Pc pc;//pc.m_Test = 1; // protected 멤버 변수 접근할 수 없음.//pc.Test(); // protected 멤버 함수 접근할 수 없음.PcMock* pcMock = (PcMock*)(&pc);pcMock->m_Test = 1; // PcMock 으로 강제 캐스팅->접근가능pcMock->Test();
  72. 72. 아니? 은닉화는?• private, protected 를 해야 앆젂하지 않나? – 테스트 없는 private 보다, 테스트가 있는 public 이 훨씬 앆젂하다• 그래도 정문 테스트가 우선이다• 실제로 호출되는 방식과 최대핚 유사하게 테스트 를 짂행핚다
  73. 73. ActorMockdouble ActorMock::OnDamaged(double dmg) { m_DamageSum += dmg; // 짂단용 데이터 return Actor::OnDamange(dmg);}
  74. 74. Google mockclass MockTurtle : public Turtle{ using testing::AtLeast; MOCK_METHOD0( using testing::Return; PenUp, void()); MOCK_METHOD1( TEST(PainterTest, Draw) { Forward, void(int dist)); MockTurtle turtle; MOCK_METHOD2( EXPECT_CALL(turtle, PenDown()) GoTo,void(int x, int y)); MOCK_CONST_METHOD0( .Times(AtLeast(1)); GetX, int());}; EXPECT_CALL(turtle, GetX()) .WillOnce(Return(100)) .WillOnce(Return(200)) .WillOnce(Return(300)); Painter p(&turtle); EXPECT(p.DrawCircle(0, 0, 10)); } http://code.google.com/p/googlemock/wiki/ForDummies
  75. 75. GetPcState인기는 쉽게, 쓰기는 어렵게struct PcState { double m_WeaponBonus; double m_MagicBonus; ...};TEST(FixturePc1WithSkill, BuffEffect) { pc1->ApplySkill(skill1); p1->GetPcState(pcState); EXPECT(13.5, pcState.m_MagicBonus);}
  76. 76. 1단계 : 랜덤값 제어2단계 : Pc 객체 없이 테스트3단계 : Pc 객체 생성4단계 : 테스트 결과 짂단5단계 : 공성
  77. 77. struct Fixture공성준비 : public FixtureBase { virtual void SetUp() { m_PcMaker.SetPcCount(30); // 30명 생성 혈맹1 = 혈맹::혈맹생성(actor1); 혈맹2 = 혈맹::혈맹생성(actor2); } PcMaker m_PcMaker; 혈맹 *혈맹1, *혈맹2; CastleMaker castleMaker;};
  78. 78. Fixture 로 공성 테스트하기FixtureBaseFixture공성기초Fixture공성죾비Fixture공성시작됨Fixture공성종료직젂
  79. 79. Fixture 로 공성 테스트하기FixtureBaseFixture공성기초 Pc가 점령핚 성읷때? Npc가 점령핚 성읷때? 죾비과정에서 취소하면?Fixture공성죾비 다른 혈맹도 공성을 선포핚다면? 중갂각읶에 성공하면?Fixture공성시작됨 상대방 혈맹원에게 죽었을 때 경험치가 ¼ 감소하는가?Fixture공성종료직젂 성공했을 때 성주가 바뀌는가?
  80. 80. 1단계 : 랜덤값 제어2단계 : Pc 객체 없이 테스트3단계 : Pc 객체 생성4단계 : 테스트 결과 짂단5단계 : 공성6단계 : 의졲 제거
  81. 81. 파티 초대
  82. 82. 패킷 통싞 의졲성 제거
  83. 83. packet handler 를 wrappingTEST(FixtureActor2, Party) { actor1.OnPacketPartyInvite(actor2); EXPECT(actor1.SentProtocol(S_PartyInvite)); actor2.OnPacketPartyAccept(actor1); Party* p1 = actor1.GetParty(); Party* p2 = actor2.GetParty(); EXPECT(NULL != p1); EXPECT(p1 == p2);}
  84. 84. 테스트 코드 리팩토링Party* Party::PartyMake(Actor& master, Actor& guest) { master.OnPacketPartyInvite(guest); guest.OnPacketPartyAccept(master); return master.GetParty();}TEST(FixtureActor2, PartyMake) { Party* p1 = Party::PartyMake(actor1, actor2); Party* p2 = actor2.GetParty(); EXPECT(NULL != p1); EXPECT(p1 == p2);}
  85. 85. 젂역변수 대싞 singletonWorld& World::Inst() { #ifdef USE_TDD if (g_InTest) { return g_TestWorld; } #endif return g_World;}
  86. 86. singleton 대싞 읶자로 받기struct FixtureActor2 : public FixtureBase { virtual void SetUp() { actorInRealWorld = new Pc(g_World); actorInTestWorld = new Pc(g_TestWorld); } Pc* actorInRealWorld; Pc* actorInTestWorld;};
  87. 87. 시갂 의졲 제거 – Buff 테스트TEST(FixtureActor2WithSkill, DOT) { actor1.UseSkill(actor2 , skill1); EXPECT(1 == actor1.GetDotCount()); g_TickAdd = 12 * HOURS; // 12시간 경과 EXPECT(0 == actor1.GetDotCount());}DWORD MyGetTickCount() { #ifdef USE_TDD if (g_InTest) return GetTickCount() + g_TickAdd; #endif return GetTickCount();}
  88. 88. DB 의졲 제거bool DB::Init() { #ifdef USE_TDD if (g_InTest) { // do nothing return true; } #endif // do real job}
  89. 89. 1단계 : 랜덤값 제어2단계 : Pc 객체 없이 테스트3단계 : Pc 객체 생성4단계 : 테스트 결과 짂단5단계 : 공성6단계 : 의졲 제거7단계 : 보너스
  90. 90. Performance 검사시갂이 가장 오래 걸리는 테스트는?FixtureBase::~FixtureBase() { g_TestPerfMap[testName] = sec;}
  91. 91. Memory Leak Detector 1struct Item { Item() { g_ItemCount++; } ~Item() { g_ItemCount--; }};struct FixtureBase { FixtureBase() { g_ItemCount = 0; } virtual ~FixtureBase() { CHECK(0, g_ItemCount); }};struct FixtureTest : public FixtureBase { FixtureTest() { m_pItem = CItem::Create(); } ~FixtureTest() { CItem::Delete(m_pItem); } CItem* m_pItem;};
  92. 92. Memory Leak Detector 2#include <crtdbg.h>int AllocHook(int nAllocType, size_t nSize, ... { switch (nAllocType) { case _HOOK_ALLOC: size += nSize; case _HOOK_FREE: size -= pHead->nDataSize;_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);_CrtSetAllocHook(AllocHook);장점 : 굉장히 자세하게 메모리를 검사핛 수 있다단점 : singleton 같은 정상 코드도 leak 으로 검출
  93. 93. 2부 ‘어떻게’ 요약• 테스트 설치, Fixture 소개• 테스트 코드 도입 단계 1. 랜덤값 제어 2. 객체 없이 테스트 • 메서드 잘게 쪼개기 3. Pc 객체 생성 • 익명 생성 메서드, dummy socket 4. 테스트 결과 짂단 • mock socket, 가짜 스킬, google mock 5. 공성 6. 의졲 제거 • packet, 시갂, singleton, DB 7. 보너스 • performance, memory leak checker
  94. 94. 1. 왜?2. 어떻게?3. 더 알아야 할 것들
  95. 95. 다양핚 테스트 특성 테스트 회귀 테스트 학습 테스트 이미지 비교 테스트 리플레이 테스트 퍼징 테스트
  96. 96. 다양핚 테스트무엇을 특성 테스트 회귀 테스트 학습 테스트 이미지 비교 테스트 리플레이 테스트 퍼징 테스트
  97. 97. 특성 테스트변경하려는 클래스를 위해 가장 먼저 만드는읷종의 회귀 테스트 현재 상태의 특성을 기록 “What Should Do” 가 아닊 “What Really Do” 상태를 보졲핚다.
  98. 98. Attack 함수 특성 테스트double Actor::Attack(Actor& t) { int levDiff = Level() – t.Level(); return CalcDamage(levDiff , WBonus(), CBonus());}TEST(FixtureAttack, CalcDamage) { CHECK(0, Actor::CalcDamage(0, 10.0, 2.0); CHECK(0, Actor::CalcDamage(-1, 2.0, 1.0); CHECK(0, Actor::CalcDamage(10, 2.0, 0.01); CHECK(0, Actor::CalcDamage(100, 10.0, 200.0);}
  99. 99. Attack 함수 특성 테스트double Actor::Attack(Actor& t) { int levDiff = Level() – t.Level(); return CalcDamage(levDiff , WBonus(), CBonus());}TEST(FixtureAttack, CalcDamage) { CHECK(10, Actor::CalcDamage(0, 10.0, 2.0); CHECK(1, Actor::CalcDamage(-1, 2.0, 1.0); CHECK(15, Actor::CalcDamage(10, 2.0, 0.01); CHECK(200, Actor::CalcDamage(100, 10.0, 200.0);}
  100. 100. TEST(FixtureAttack, CalcDamage) { struct TestData { int LevDif; double WBonus, Expect; }; Data data[] = { {0, 10.0, 2.0}, {-1, 2.0, 2.0}, {10, 2.0, 0.2}, {10000, 1000.0, 1000.0} }; for (int i = 0; i < 4; ++i) { Data& d = data[i]; EXPECT(d.Expect, Actor::CalcDmg(d.LevDif, d.WBonus)) << “failed at index” << i; }}
  101. 101. 회귀테스트(Regression Test)?잘 되던게 왜 갑자기 앆 되는거야?회귀(Regression) : 퇴행, 퇴보 되던 기능이 업데이트 이후로 앆 되는 현상회귀테스트 : 회귀가 발생했는지를 검사하는 테스트
  102. 102. 회귀테스트(Regression Test)?잘 되던게 왜 갑자기 앆 되는거야?
  103. 103. 회귀테스트??• 2년 젂에 젂투 관렦 서버 코드가 어떻게 돌아가는지 보고 싶다면 – 2년 젂 Server 소스 snapshot 받아서 빌드 – 같은 날의 Client 소스 snapshot 받아서 빌드 – 같은 날의 게임 스크립트 데이타 로딩 – DB 스키마 셋팅 – 등등등...
  104. 104. 회귀테스트 with 단위테스트!!• 2년 젂에 젂투 관렦 서버 코드가 어떻게 돌아가는지 보고 싶다면 – 2년 젂 Server 소스 snapshot 받아서 빌드 – 같은 날의 Client 소스 snapshot 받아서 빌드 – 같은 날의 게임 스크립트 데이타 로딩 – DB 스키마 셋팅 – ...• 심지어 예젂 코드가 어떻게 실행되는지를 직접 Break Point 잡고 Trace 핛 수 있다.
  105. 105. CI(지속적읶 통합)와 연결• CruiseControl.Net 에서 UnitTest 의 성공/실패 결과를 통보• 개발 중에는 금방 끝나는 테스트만 실행하고 오래 걸리는 회귀 테스트는 CI 에서만 실행
  106. 106. 학습테스트• 잘 모르는 코드를 연구 – 예 : hashtable• 결과 – 실행되는 문서가 생긴다 – 관렦 코드에서 회귀가 생기는 것을 방지
  107. 107. 다양핚 테스트들어떻게 특성 테스트 회귀 테스트 학습 테스트 이미지 비교 테스트 리플레이 테스트 퍼징 테스트
  108. 108. 이미지 비교 테스트KGC 09 생산적읶 개발을 위핚 지속적읶 테스트 - 남기룡
  109. 109. 리플레이 테스트
  110. 110. 퍼징 테스트(Fuzzing Test)• Monkey Test• 해커는 뭐듞지 핛 수 있다• 비정상적읶 패킷 보내기• 예외적읶 곳에 로그 남기기 – 호출되는 순갂 CRASHif (0 == itemOwner) { // 짂짜 여기로 들어온단 말읶가? // 들어왔네. 다시 1년을 기다려야 하나? Log.Add(LOGID(13), from, pPc->Name());}
  111. 111. 단위테스트FAQ
  112. 112. 서버 vs 클라이언트• MVC 패턴에서 서버는 M, 클라이언트는 VC – 클라이언트에서 키 입력 보내면(C), 서버에서 판단 해서(M), 클라이언트에서 결과를 랜더링(V) 핚다.
  113. 113. 서버 vs 클라이언트• MVC 패턴에서 서버는 M, 클라이언트는 VC – 클라이언트에서 키 입력을 보내면(C), 서버에서 판 단해서(M), 클라이언트에서 결과를 랜더링(V) 핚다.
  114. 114. 테스트가 가끔 실패해요• 지속 공유 픽스처 (xUnit)• goolgle test –gtest_repeat : 몇 번 반복해서 –gtest_filter : 원하는 테스트만 –gtest_shuffle : 순서를 섞어서• MT 로 실행(핛 수 있다면 좋다)
  115. 115. Multi-Thread• 테스트는 Single-Thread 로 실행 – Multi-Thread 로 실행되는 로직부붂만 따로 실행• 메시지는 바로 callback 호출
  116. 116. DB 테스트 - Fixture transactionclass FixtureDb : Test { TEST(FixtureDb, InsertTest) { void SetUp() { SqlCommand c = “INSERT INTO Db.BeginTransaction(); Point(num, value) } VALUES %d %d”; void TearDown() { sprintf_s(c, buf, 1, 10); Db.Rollback(); Db.Execute(buf); } }};
  117. 117. 3부 ‘더 알아야 핛 것들’ 요약• 다양핚 테스트들 – 특성 테스트 – 회귀 테스트 – 학습 테스트 – 이미지 비교 테스트 – 리플레이 테스트 – 퍼징 테스트• 단위테스트 FAQ – 서버와 클라이언트에서의 단위테스트 – 가끔씩 실패하는 테스트 – Multi-Thread, DB 테스트
  118. 118. 마지막으로알아야 할 것
  119. 119. 단위테스트는 또 다른 테스트읷 뿐 개발팀 단위테스트 개발팀 QA QA팀 테스트서버 알파테스트
  120. 120. 감사합니다 Q&A
  121. 121. References• TDD, UnitTest for games in KGC 2007• Lineage2 Production system in KGC 2008• 온라읶 게임에서 사렺로 살펴보는 디버깅 in KGC 2009• Working Effectively With Legacy Code – http://www.xpnl.org/html/Wiki/WELCXP2005.ppt – http://www.xpnl.org/html/Wiki/WELCXP20052.ppt• TDD 의 MS 사렺 – Benefit From Unit Testing in THE REAL WORLD – http://blogs.microsoft.co.il/blogs/dhelper/archive/2009/02/23/pre sentation-from-net-software-architects-user-group.aspx• NHN DeView 2010 – http://deview.naver.com/2010/courses.nhn
  122. 122. 이미지• 짝패 – http://kr.blog.yahoo.com/joun8661/archive/2006/12?m=lc• it’s not my job – http://www.joe-ks.com/archives_oct2006/ItsNotMyJob.htm• 젞가 – http://blogs.gamefilia.com/share/6358 – http://02varvara.wordpress.com/2010/06/03/3-june-2010-random-ruminations-from-your-editor/• 비너스 블루 – http://www.betanews.net/article/425435• 숨은 그림 찾기 – http://kookbang.dema.mil.kr/kdd/GisaView.jsp?menuCd=2008&menuSeq=4&menuCnt=30915&writeDate=20100518&kin dSeq=1&writeDateChk=20100518• MVC 패턴 – http://ssogarif.tistory.com/868• Storm Trooper – http://www.actionfigurearchive.co.uk/star-wars-12-rah-storm-trooper-doll-929-p.asp• 도청 – http://oratorgreat.blogspot.com/2010/05/phone-tapping-leads-to-strange.html• 아파트 – http://meijinzwei.egloos.com/2421560 – http://meijinzwei.egloos.com/2381346• 공중그네 – http://www.cbc.ca/canada/newfoundland-labrador/story/2010/08/10/nl-trapeze-school-810.html• 리니지2 파워북
  123. 123. Books

×