320x100
GoogleTest를 써라
TEST(AccountTest, ConstructorWidthBalance)
{
Account account = Account(10000);
if (account.Balance() != 10000)
{
FAIL();
}
}
장점
xUnit을 포괄하는 기능
GoogleMock과의 연계
Google이 개발
의존성을 끊어라
TEST(Player, Fall)
{
Player player;
player.SetZ(1000);
player.Fall(1.0f);
EXPECT_EQ(900, player.GetZ());
}
Player::Player(Server* s, string id)
{
s->ReceivePlayerInfo(this, id);
}
Player 객체를 생성할 때는 단독으로 생성하는게 거의 불가능하다.
다른 객체와의 연관성이 있기 마련인데, 이를 해결할 수 있는 방법은 가짜 객체다.
class Server
{
void ReceovePlayerInfo(Player* p);
};
를 다음과 같이 변경한다.
class Server
{
virtual void ReceivePlayerInfo(Player* p);
};
class NullServer : public Server
{
virtual void ReceivePlayerInfo(Player* p) {};
}
TEST(Player, Fall)
{
NullServer nullServer;
Player player(&nullServer, "");
player.SetZ(1000);
player.Fall(1.0f);
EXPECT_EQ(900, player.GetZ());
}
Player::Player(Server* s, string id)
{
s->ReceivePlayerInfo(this, id);
}
테스트 작성을 힘들게 하는 유형
- 객체 생성 불가
테스트 코드 작성의 시작은 객체 생성 (여기서 막히는 경우가 다반사)
어떤 클래스가 외부에 너무 의존성이 커서 객체를 쉽게 생성할 수 없는 문제
주로 Player, Game, World등의 이름이 붙은 클래스에서 발생
=> 의존성 주입으로 해결(생성자 또는 Setter 주입)
class Player { Player() { _server = new Server("192.168.0.1"); _id = g_session->GetPlayerID(); _server->GetPlayerInfo(this, _id); } // 다음과 같이 변환 Player(Server* server, Session* session) { _server = server; _id = session->GetPlayerID(); _server->GetPlayerInfo(this, _id); } };
=> 가짜 객체(Mock)
또는 프레임 워크를 사용한다 (GoogleMock)TEST() { NullServer nullServer; NullSession nullSession; Player player(&nullServer, &nullSession); }
class Server { void GetPlayerInfo(Player*, string); }; class MockServer : public Server { MOCK_METHOD2(GetPlayerInfo, void(Player*, string)); }; TEST() { MockServer mockServer; MockSession mockSession; EXPECT_CALL(mockSession, GetPlayerID()).WillOnce(Return("haha")); Player player(&mockServer, &mockSession); EXPECT_CALL(mockServer, GetPlayerInfo(&player, "haha")); }
- 자유 함수 호출
send(), recv(), fopen(), CreateWindow(), Direct3DCreate()..
=> 래핑
class WindowAPI { virtual HWND CreateWindow(...); }; void Game::Init(WindowAPI* api) { api->CreateWindow(...); } TEST() { Game game; NullWindowAPI api; game.Init(&api); }
- 전역 변수 접근
Item* Inventory::DropItem(string name) { if (g_player.IsFlying()) { return NULL; } } TEST() { Inventory inventory; Item* item = new Item("장미칼"); inventory.AddItem(item); EXPECT_EQ(item, inventory.DropItem("장미칼")); }
WORKING EFFECTIVELY WITH LEGACY CODE
CppUnit 원작자, 레거시 코드 테스트 걸기, 24개의 종속성 끊기 기술
시행착오로 얻은 행동요령들
- 막대 주기 최소화
- 코드작성 중 리팩토링 금지
- 복합한 Mock이면 테스트하라
- 객체 생명 주기는 스마트포인터로
출처: 홍종찬 / 넥슨코리아
http://ndcreplay.nexon.com/NDC2013/sessions/NDC2013_0048.html#c=NDC2013
320x100
'NDC > Dev' 카테고리의 다른 글
[NDC 2016] 구형맵에서는 어떻게 길을 찾아야 하나요? (0) | 2023.01.17 |
---|---|
[NDC 2016] 유니티, iOS에서 LINQ 사용하기 (0) | 2023.01.15 |
[NDC 2014] 멀티쓰레드 프로그래밍이 왜이리 힘드나요? (0) | 2022.10.18 |
[NDC 2014] 파이썬과 친구들 (0) | 2021.12.06 |
[NDC2013] 테스트 꾸준히 잘하는 법 (0) | 2021.11.14 |