GoogleTest TEST_F() - 예제로 배우는 C++ 테스트 픽스처
앞서 예제를 통해 TEST()와 TEST_F()의 차이점까지 이해가 되었다면 간단하지만 조금 더 실무적인 예제를 통해 TEST_F()의 사용법을 이해해 볼 수 있습니다. 또한 결과를 보기위해 다양한 방법으로 컴파일 할 수 있지만 좀 더 사용 용도가 많은 구조를 만들어 보기위해 프로젝트 내에 src, test가 분리되며 각 폴더에 CMakeLists.txt를 사용해 보겠습니다.
1. 프로젝트 구조
이번 예제는 CMake 기반으로 구성하고, src와 test 폴더를 분리하여 실제 프로젝트와 유사한 구조로 만들어봅니다.
MyProject/
├── src
│ ├── CMakeLists.txt
│ ├── UserManager.h
│ └── UserManager.cpp
├──test
│ ├── CMakeLists.txt
│ ├── test_user_manager.cpp
│ └── test_main.cpp
└── CMakeLists.txt
- CMakeLists.txt: 프로젝트 루트에 위치하며, 하위 디렉터리(src, test)를 포함해 전체 빌드를 관리합니다.
- src/: 실제 기능을 구현하는 C++ 소스 코드가 들어갑니다.
- test/: 구글테스트 기반으로 테스트 코드를 작성하는 폴더입니다.
1.1 최상위 CMakeLists.txt 예시
cmake_minimum_required(VERSION 3.10)
project(MyProject)
# C++17 이상 사용 예시
set(CMAKE_CXX_STANDARD 17)
# 구글테스트를 FetchContent로 자동 다운로드하는 예시
# (직접 설치된 googletest를 쓰는 경우, 아래 로직 대신 find_package(GTest ...) 등을 사용)
include(FetchContent)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG release-1.12.1 # 원하는 버전
)
FetchContent_MakeAvailable(googletest)
# src 디렉터리 포함
add_subdirectory(src)
# test 디렉터리 포함
enable_testing()
add_subdirectory(test)
참고:
Cygwin 환경에서 이미 googletest 패키지를 설치했다면 FetchContent로 굳이 내려받지 않고, find_package(GTest REQUIRED) 식으로 구성해도 됩니다.
1.2 src/CMakeLists.txt 예시
# src/CMakeLists.txt
add_library(MyLibrary
UserManager.cpp
)
target_include_directories(MyLibrary
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
)
- MyLibrary라는 이름의 라이브러리를 생성합니다.
- UserManager.cpp를 컴파일하고, 필요한 헤더 검색 경로를 설정합니다.
1.3 test/CMakeLists.txt 예시
# test/CMakeLists.txt
add_executable(MyTests
test_user_manager.cpp
)
# 테스트 코드가 MyLibrary를 링크하도록 설정
target_link_libraries(MyTests
PRIVATE
MyLibrary
gtest
gtest_main
pthread # (Cygwin에서 필요할 수 있음)
)
# CTest 사용 가능하도록
add_test(NAME MyTests COMMAND MyTests)
- MyTests라는 실행 파일에 test_user_manager.cpp를 포함합니다.
- MyLibrary와 gtest, gtest_main 라이브러리를 링크합니다.
- add_test를 통해 CTest에서 테스트를 수행할 수 있도록 등록합니다.
2. UserManager 클래스
UserManager는 여러 사용자 정보를 관리하는 간단한 클래스로, 아래 기능을 제공합니다.
- 사용자 추가: AddUser()
- 사용자 삭제: RemoveUser()
- 사용자 조회: GetUser()
- 총 사용자 수 확인: GetUserCount()
여기서는 단순하게 사용자 정보를 std::map<int, std::string>에 저장하며, int를 사용자 ID, std::string을 사용자 이름으로 가정합니다.
2.1 src/UserManager.h
#ifndef USER_MANAGER_H
#define USER_MANAGER_H
#include <map>
#include <string>
#include <stdexcept>
class UserManager {
public:
// 사용자 추가 (중복 ID는 예외 처리)
void AddUser(int userId, const std::string& userName);
// 사용자 삭제 (없는 ID 삭제 시 false 반환)
bool RemoveUser(int userId);
// 사용자 조회 (없는 ID 조회 시 예외)
std::string GetUser(int userId) const;
// 전체 사용자 수
size_t GetUserCount() const;
private:
// ID -> 사용자 이름 매핑
std::map<int, std::string> users_;
};
#endif // USER_MANAGER_H
2.2 UserManager.cpp
#include "UserManager.h"
void UserManager::AddUser(int userId, const std::string& userName) {
// 이미 존재하는 ID라면 예외
if (users_.find(userId) != users_.end()) {
throw std::runtime_error("User ID already exists.");
}
users_[userId] = userName;
}
bool UserManager::RemoveUser(int userId) {
auto it = users_.find(userId);
if (it == users_.end()) {
return false; // 존재하지 않음
}
users_.erase(it);
return true;
}
std::string UserManager::GetUser(int userId) const {
auto it = users_.find(userId);
if (it == users_.end()) {
throw std::runtime_error("User not found.");
}
return it->second;
}
size_t UserManager::GetUserCount() const {
return users_.size();
}
3. 테스트 코드 - TEST_F() 예제
이번에는 TEST_F()를 사용해 테스트 픽스처(Test Fixture)를 구성해봅니다. 아래는 test_user_manager.cpp 파일의 예시입니다.
3.1 test_user_manager.cpp
#include <gtest/gtest.h>
#include "../src/UserManager.h"
// 테스트 픽스처 클래스
class UserManagerTest : public ::testing::Test {
protected:
void SetUp() override {
// 각 테스트마다 새로운 UserManager 객체를 생성
userManager = new UserManager();
// 테스트 공통으로 사용할 샘플 데이터 추가
userManager->AddUser(1, "Alice");
userManager->AddUser(2, "Bob");
}
void TearDown() override {
// 동적 할당 해제
delete userManager;
}
UserManager* userManager;
};
// TEST_F()를 이용해 공통 픽스처인 userManager를 테스트에 활용
TEST_F(UserManagerTest, AddUser_Success) {
// 이미 SetUp()에서 1, 2 ID를 추가해둠
EXPECT_NO_THROW(userManager->AddUser(3, "Charlie"));
// 총 3명이 되었는지 확인
EXPECT_EQ(userManager->GetUserCount(), 3u);
}
TEST_F(UserManagerTest, AddUser_DuplicateID) {
// 중복 ID 추가 시도 (이미 1번 ID가 존재)
EXPECT_THROW(userManager->AddUser(1, "AnotherAlice"), std::runtime_error);
}
TEST_F(UserManagerTest, RemoveUser_Success) {
// 2번 ID 제거
bool removed = userManager->RemoveUser(2);
EXPECT_TRUE(removed);
// 제거 후 사용자 수 확인
EXPECT_EQ(userManager->GetUserCount(), 1u);
}
TEST_F(UserManagerTest, RemoveUser_NotExisting) {
// 999번 ID는 존재하지 않음
bool removed = userManager->RemoveUser(999);
EXPECT_FALSE(removed);
}
TEST_F(UserManagerTest, GetUser_Success) {
// 1번 ID("Alice")가 맞는지 확인
EXPECT_EQ(userManager->GetUser(1), "Alice");
}
TEST_F(UserManagerTest, GetUser_NotFound) {
// 존재하지 않는 999번 ID 조회 시 예외
EXPECT_THROW(userManager->GetUser(999), std::runtime_error);
}
3.2 테스트 픽스처 사용 요령
- UserManagerTest는 ::testing::Test를 상속받으며, SetUp()에서 userManager 객체를 생성하고 샘플 사용자 2명을 미리 등록합니다.
- 각 테스트(TEST_F)는 이 픽스처를 공유하되, 매번 독립된 환경(새로운 UserManager)으로 실행됩니다.
- 테스트가 끝나면 TearDown()에서 해제하므로, 테스트끼리 서로 영향을 주지 않습니다.
4. 빌드 및 실행
빌드 및 실행을 위해 Cygwin 터미널에서 프로젝트 루트(MyProject/)로 이동 후 다음 명령어를 순서대로 실행합니다.
- 성공적으로 빌드되면 MyTests라는 실행 파일이 생성됩니다.
$ mkdir build && cd build
$ cmake ..
$ make
$ ./MyTests
[==========] Running 6 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 6 tests from UserManagerTest
[ RUN ] UserManagerTest.AddUser_Success
[ OK ] UserManagerTest.AddUser_Success (0 ms)
[ RUN ] UserManagerTest.AddUser_DuplicateID
[ OK ] UserManagerTest.AddUser_DuplicateID (0 ms)
[ RUN ] UserManagerTest.RemoveUser_Success
[ OK ] UserManagerTest.RemoveUser_Success (0 ms)
[ RUN ] UserManagerTest.RemoveUser_NotExisting
[ OK ] UserManagerTest.RemoveUser_NotExisting (0 ms)
[ RUN ] UserManagerTest.GetUser_Success
[ OK ] UserManagerTest.GetUser_Success (0 ms)
[ RUN ] UserManagerTest.GetUser_NotFound
[ OK ] UserManagerTest.GetUser_NotFound (0 ms)
[----------] 6 tests from UserManagerTest (0 ms total)
[----------] Global test environment tear-down
[==========] 6 tests from 1 test suite ran. (0 ms total)
[ PASSED ] 6 tests.
5. 마무리 및 확장 아이디어
- TEST_F()를 사용하면 여러 테스트에서 공통으로 사용할 객체나 리소스를 쉽게 관리할 수 있어 실무에서도 매우 자주 활용됩니다.
- 이번 예제에서는 사용자 ID/이름 관리의 간단한 시나리오를 다뤘지만, 실제 업무 환경에서는 데이터베이스 연결, 서버 소켓, 파일 입출력, 복잡한 계산 로직 등을 픽스처로 설정해두고 재사용하는 경우가 많습니다.
- 픽스처가 복잡해지면 SetUpTestSuite()나 TearDownTestSuite()(클래스 스코프에서 한 번만 실행) 같은 함수도 고려해볼 수 있습니다.
- 추가로, TEST_P()와 INSTANTIATE_TEST_SUITE_P() 등을 사용하면 매개변수화된 테스트도 손쉽게 구현할 수 있으니, 필요하다면 구글테스트의 공식 문서를 참고해보세요.
이상으로, C++ 프로젝트에서 Google Test의 TEST_F()를 사용해 테스트 픽스처를 구성하는 실무 예시를 살펴보았습니다. 테스트 코드가 점점 늘어가더라도, 픽스처를 잘 관리하면 테스트의 가독성과 유지 보수성이 크게 향상됩니다. 필요에 따라 더 복잡한 시나리오나 환경 설정을 추가하면서 자신의 프로젝트에 맞춰 확장해보길 바랍니다.
'TDD 테스트주도개발 > TDD for C++' 카테고리의 다른 글
[GMock이해]Mock 객체 종류를 이해하자 (0) | 2025.06.13 |
---|---|
[GMock이해]ON_CALL vs EXPECT_CALL 차이 (0) | 2025.06.13 |
[googletest문법5]TEST() 이해2 (0) | 2025.03.26 |
[googletest문법4]파라메터 이용 TEST_P() 사용법 (1) | 2025.03.13 |
[googletest문법3]테스트 픽스처 TEST_F() 사용법 (0) | 2025.03.10 |