Google Test(GTest) 활용 가이드: 설치부터 테스트 작성까지

Google Test(GTest) 설치

Google Test(GTest)는 C++ 개발을 위한 강력한 단위 테스트 프레임워크입니다. GTest를 시스템에 설치하는 과정은 다음과 같습니다.

1. 소스 코드 확보

GTest의 최신 버전 소스 코드를 GitHub에서 클론합니다. 일반적으로 특정 릴리스 버전을 사용하는 것이 안정적입니다. 예를 들어, 1.11.0 릴리스 버전을 사용합니다.

git clone https://github.com/google/googletest
cd googletest
git checkout release-1.11.0 # 최신 안정 버전으로 체크아웃

2. 빌드 및 라이브러리 생성

GTest를 빌드하려면 CMake가 필요합니다. 시스템에 CMake가 설치되어 있지 않다면 먼저 설치합니다 (예: brew install cmake 또는 sudo apt-get install cmake).

googletest 디렉토리 내에 build 디렉토리를 생성하고 CMake를 실행하여 Makefile을 생성한 후, make 명령으로 라이브러리를 빌드합니다.

mkdir build
cd build
cmake .. # CMakeLists.txt 파일이 있는 상위 디렉토리 지정
make

위 과정이 성공적으로 완료되면 libgtest.alibgtest_main.a 두 개의 정적 라이브러리가 생성됩니다.

3. 시스템 경로로 라이브러리 및 헤더 파일 복사

생성된 라이브러리와 헤더 파일을 시스템 전역에서 접근할 수 있는 경로로 복사합니다.

sudo cp libgtest*.a /usr/local/lib/
sudo cp -a ../googletest/include/gtest /usr/local/include/

참고: 시스템에 따라 /usr/local/lib/usr/local/include 대신 /usr/lib/usr/include 경로를 사용할 수도 있습니다. 정확한 경로를 찾으려면 find . -name "libgtest*.a" 명령을 활용하세요.

설치 확인: 간단한 테스트 예제

설치가 제대로 되었는지 확인하기 위해 간단한 GTest 코드를 작성하고 컴파일해봅니다.

#include <gtest/gtest.h>

// 테스트할 대상 함수
int multiply(int x, int y) {
    return x * y;
}

// 테스트 케이스 정의
TEST(CalculationTest, HandlesPositiveMultiplication) {
    EXPECT_EQ(multiply(3, 4), 12);
    EXPECT_NE(multiply(2, 5), 9);
}

TEST(CalculationTest, HandlesZeroMultiplication) {
    EXPECT_EQ(multiply(7, 0), 0);
}

int main(int argc, char **argv) {
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

위 코드를 sample_test.cpp로 저장하고 다음 명령으로 컴파일 및 실행합니다.

g++ sample_test.cpp -o sample_test -lgtest -lgtest_main -pthread
./sample_test

성공적으로 실행되면 다음과 유사한 결과가 출력됩니다.

[==========] Running 2 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 2 tests from CalculationTest
[ RUN      ] CalculationTest.HandlesPositiveMultiplication
[       OK ] CalculationTest.HandlesPositiveMultiplication (0 ms)
[ RUN      ] CalculationTest.HandlesZeroMultiplication
[       OK ] CalculationTest.HandlesZeroMultiplication (0 ms)
[----------] 2 tests from CalculationTest (0 ms total)
[----------] Global test environment tear-down
[==========] 2 tests from 1 test suite ran. (0 ms total)
[  PASSED  ] 2 tests.

GTest의 핵심 개념: 단언(Assertion)

GTest는 테스트 대상 코드의 동작을 검증하기 위한 단언 매크로를 제공합니다. 단언은 특정 조건이 참(True)인지, 두 값이 같은지 등을 확인하며, 실패할 경우 GTest는 오류 메시지와 함께 해당 코드의 파일명 및 줄 번호를 출력합니다. 또한, 사용자 정의 오류 메시지를 추가할 수도 있습니다.

단언은 일반적으로 두 가지 형태로 제공됩니다: ASSERT_*EXPECT_*.

  • ASSERT_* (치명적 단언): 이 단언이 실패하면 현재 테스트 함수는 즉시 중단됩니다. 이는 이후의 테스트 실행이 무의미하거나 예상치 못한 부작용을 일으킬 수 있는 상황에 적합합니다.
  • EXPECT_* (비치명적 단언): 이 단언이 실패하더라도 현재 테스트 함수는 계속 실행됩니다. 하나의 테스트에서 여러 오류를 보고해야 할 때 유용하며, 일반적으로 더 권장되는 방식입니다.

테스트 작성 시, 단언 실패가 발생하면 더 이상 진행할 필요가 없는 경우에는 ASSERT_*를, 가능한 한 많은 오류를 발견하고 싶을 때는 EXPECT_*를 사용합니다.

GTest 단언 매크로 종류

1. 부울(Boolean) 값 검사

치명적 단언 (Fatal) 비치명적 단언 (Nonfatal) 검증 내용
ASSERT_TRUE(condition); EXPECT_TRUE(condition); condition이 참(true)인지 확인
ASSERT_FALSE(condition); EXPECT_FALSE(condition); condition이 거짓(false)인지 확인

2. 수치형 데이터 비교

치명적 단언 (Fatal) 비치명적 단언 (Nonfatal) 검증 내용
ASSERT_EQ(expected, actual); EXPECT_EQ(expected, actual); expected == actual
ASSERT_NE(val1, val2); EXPECT_NE(val1, val2); val1 != val2
ASSERT_LT(val1, val2); EXPECT_LT(val1, val2); val1 < val2
ASSERT_LE(val1, val2); EXPECT_LE(val1, val2); val1 <= val2
ASSERT_GT(val1, val2); EXPECT_GT(val1, val2); val1 > val2
ASSERT_GE(val1, val2); EXPECT_GE(val1, val2); val1 >= val2

3. 문자열 비교

치명적 단언 (Fatal) 비치명적 단언 (Nonfatal) 검증 내용
ASSERT_STREQ(expected_str, actual_str); EXPECT_STREQ(expected_str, actual_str); 두 C-스타일 문자열의 내용이 동일한지 확인
ASSERT_STRNE(str1, str2); EXPECT_STRNE(str1, str2); 두 C-스타일 문자열의 내용이 다른지 확인
ASSERT_STRCASEEQ(expected_str, actual_str); EXPECT_STRCASEEQ(expected_str, actual_str); 두 C-스타일 문자열의 내용이 동일한지 (대소문자 무시) 확인
ASSERT_STRCASENE(str1, str2); EXPECT_STRCASENE(str1, str2); 두 C-스타일 문자열의 내용이 다른지 (대소문자 무시) 확인

4. 예외(Exception) 검사

치명적 단언 (Fatal) 비치명적 단언 (Nonfatal) 검증 내용
ASSERT_THROW(statement, exception_type); EXPECT_THROW(statement, exception_type); statement가 특정 exception_type의 예외를 발생시키는지 확인
ASSERT_ANY_THROW(statement); EXPECT_ANY_THROW(statement); statement가 어떤 종류의 예외라도 발생시키는지 확인
ASSERT_NO_THROW(statement); EXPECT_NO_THROW(statement); statement가 예외를 발생시키지 않는지 확인

5. 부동소수점(Floating-point) 값 검사

치명적 단언 (Fatal) 비치명적 단언 (Nonfatal) 검증 내용
ASSERT_FLOAT_EQ(expected, actual); EXPECT_FLOAT_EQ(expected, actual); float 값이 거의 동일한지 확인
ASSERT_DOUBLE_EQ(expected, actual); EXPECT_DOUBLE_EQ(expected, actual); double 값이 거의 동일한지 확인
ASSERT_NEAR(val1, val2, abs_error); EXPECT_NEAR(val1, val2, abs_error); val1val2의 차이가 주어진 abs_error를 초과하지 않는지 확인

이 외에도 타입 검사, 술어(predicate) 검사 등 다양한 단언 매크로를 제공합니다.

GTest의 이벤트(Event) 메커니즘

GTest는 테스트 실행 전후에 특정 코드를 실행할 수 있는 이벤트 후크(hook)를 제공합니다. 이는 테스트 환경 설정 및 해제에 유용합니다. 주요 이벤트 유형은 다음과 같습니다.

1. 전역(Global) 이벤트

모든 테스트가 시작하기 전과 끝난 후에 한 번씩 실행되는 이벤트입니다. testing::Environment 클래스를 상속받아 SetUp()TearDown() 메서드를 구현합니다. 이 환경은 main 함수에서 testing::AddGlobalTestEnvironment()를 통해 등록합니다.

  • SetUp(): 모든 테스트 케이스가 실행되기 전에 호출됩니다.
  • TearDown(): 모든 테스트 케이스 실행이 완료된 후에 호출됩니다.

2. 테스트 스위트(Test Suite) 이벤트

특정 테스트 스위트(즉, TEST_F로 묶인 테스트 그룹)의 첫 번째 테스트가 실행되기 전과 마지막 테스트가 끝난 후에 한 번씩 실행됩니다. testing::Test 클래스를 상속받는 클래스에서 정적 메서드 SetUpTestCase()TearDownTestCase()를 구현합니다.

  • SetUpTestCase(): 해당 테스트 스위트의 첫 번째 TEST_F가 실행되기 전에 호출됩니다.
  • TearDownTestCase(): 해당 테스트 스위트의 마지막 TEST_F가 실행된 후에 호출됩니다.

3. 테스트 케이스(Test Case) 이벤트

각각의 개별 TEST_F 또는 TEST 매크로가 실행되기 전후에 호출됩니다. 이는 testing::Test 클래스를 상속받는 클래스에서 SetUp()TearDown() 메서드를 구현합니다.

  • SetUp(): 각 TEST_F 또는 TEST가 실행되기 전에 호출됩니다.
  • TearDown(): 각 TEST_F 또는 TEST가 실행된 후에 호출됩니다.

다음 예제는 위의 세 가지 이벤트 메커니즘을 모두 활용하는 방법을 보여줍니다.

#include <gtest/gtest.h>
#include <iostream>
#include <string>

// 테스트에서 사용할 리소스 클래스 (예: 사용자 설정)
class UserConfig {
public:
    UserConfig(const std::string& name = "Guest", int id = 0) : userName(name), userId(id) {
        std::cout << "UserConfig created: " << userName << " (ID: " << userId << ")" << std::endl;
    }
    ~UserConfig() {
        std::cout << "UserConfig destroyed: " << userName << " (ID: " << userId << ")" << std::endl;
    }
    void displayInfo() const {
        std::cout << "Current User: " << userName << ", ID: " << userId << std::endl;
    }
private:
    std::string userName;
    int userId;
};

// 1. 전역 이벤트 정의
class GlobalAppEnvironment : public testing::Environment {
public:
    void SetUp() override {
        std::cout << "--- GlobalAppEnvironment SetUp (Before all tests) ---" << std::endl;
        // 전역 리소스 초기화 (예: 데이터베이스 연결)
    }
    void TearDown() override {
        std::cout << "--- GlobalAppEnvironment TearDown (After all tests) ---" << std::endl;
        // 전역 리소스 해제
    }
};

// 2. 테스트 스위트 및 테스트 케이스 이벤트 정의
// UserConfig 객체를 스위트 전체에서 공유하기 위해 static으로 선언
static UserConfig* globalUserConfig = nullptr; 

class UserFeatureTests : public testing::Test {
protected:
    // 테스트 스위트 시작 전 (한 번만 실행)
    static void SetUpTestCase() {
        std::cout << "--- UserFeatureTests SetUpTestCase (Before first test in suite) ---" << std::endl;
        globalUserConfig = new UserConfig("Admin", 101); // 스위트 전역 리소스 생성
    }

    // 테스트 스위트 종료 후 (한 번만 실행)
    static void TearDownTestCase() {
        std::cout << "--- UserFeatureTests TearDownTestCase (After last test in suite) ---" << std::endl;
        delete globalUserConfig; // 스위트 전역 리소스 해제
        globalUserConfig = nullptr;
    }

    // 각 테스트 케이스 시작 전
    void SetUp() override {
        std::cout << "--- UserFeatureTests SetUp (Before each test case) ---" << std::endl;
        // 각 테스트 케이스마다 필요한 설정 (예: 테스트 데이터 초기화)
    }

    // 각 테스트 케이스 종료 후
    void TearDown() override {
        std::cout << "--- UserFeatureTests TearDown (After each test case) ---" << std::endl;
        // 각 테스트 케이스마다 사용된 리소스 정리
    }
};

// 테스트 스위트 이벤트가 적용되는 테스트 케이스
TEST_F(UserFeatureTests, AdminUserCanViewProfile) {
    ASSERT_NE(globalUserConfig, nullptr);
    globalUserConfig->displayInfo();
    EXPECT_EQ("Admin", "Admin"); // 실제 로직 대신 간단한 단언
    std::cout << "Running AdminUserCanViewProfile test" << std::endl;
}

TEST_F(UserFeatureTests, AdminUserCanEditSettings) {
    ASSERT_NE(globalUserConfig, nullptr);
    globalUserConfig->displayInfo();
    EXPECT_TRUE(true); // 실제 로직 대신 간단한 단언
    std::cout << "Running AdminUserCanEditSettings test" << std::endl;
}

int main(int argc, char **argv) {
    // 전역 이벤트 등록
    testing::AddGlobalTestEnvironment(new GlobalAppEnvironment);

    // GTest 초기화
    testing::InitGoogleTest(&argc, argv);

    // 모든 테스트 실행
    return RUN_ALL_TESTS();
}

이 코드를 실행하면 전역, 테스트 스위트, 개별 테스트 케이스의 SetUp/TearDown 메서드들이 어떤 순서로 호출되는지 콘솔 출력으로 확인할 수 있습니다.

GTest의 파라미터화된 테스트 (Parameterized Test)

동일한 테스트 로직을 여러 다른 입력 값으로 반복해서 실행해야 할 때 파라미터화된 테스트를 사용합니다. 이는 테스트 코드의 중복을 줄이고 유지보수성을 높입니다.

파라미터화된 테스트를 구현하려면 testing::TestWithParam<T> 클래스를 상속받는 테스트 클래스를 정의해야 합니다. 여기서 T는 파라미터의 타입입니다.

#include <gtest/gtest.h>
#include <string>
#include <algorithm> // std::reverse를 위해 포함

// 테스트할 대상 함수: 문자열이 회문(Palindrome)인지 확인
bool isPalindrome(const std::string& str) {
    std::string temp_str = str;
    // 대소문자 무시 및 공백 제거 등의 전처리 로직을 추가할 수 있습니다.
    // 여기서는 간단히 문자열 자체를 뒤집어 비교합니다.
    std::reverse(temp_str.begin(), temp_str.end());
    return str == temp_str;
}

// 1. TestWithParam<T>를 상속받는 테스트 클래스 정의
class PalindromeCheckerTest : public testing::TestWithParam<std::string> {};

// 2. TEST_P 매크로를 사용하여 파라미터화된 테스트 케이스 정의
TEST_P(PalindromeCheckerTest, ChecksForValidPalindromes) {
    std::string test_string = GetParam(); // GetParam()으로 현재 파라미터 값을 가져옴
    EXPECT_TRUE(isPalindrome(test_string)) << "Expected \"" << test_string << "\" to be a palindrome.";
}

// 3. INSTANTIATE_TEST_SUITE_P 매크로를 사용하여 파라미터 값 목록 지정 (GTest 1.10 이상에서는 INSTANTIATE_TEST_SUITE_P 사용 권장)
// 첫 번째 인자: 테스트 그룹의 고유한 이름 접두사 (여기서는 "PalindromeValues")
// 두 번째 인자: TestWithParam을 상속받은 테스트 클래스 이름 (PalindromeCheckerTest)
// 세 번째 인자: 파라미터 생성기 (testing::Values, testing::Range, testing::Combine 등)
INSTANTIATE_TEST_SUITE_P(
    PalindromeValues, // 테스트 스위트 인스턴스 이름
    PalindromeCheckerTest, // TestWithParam 상속 클래스
    testing::Values("madam", "racecar", "level", "refer", "deified") // 파라미터 값들
);

// 실패 케이스를 위한 또 다른 테스트 스위트 정의 (선택 사항)
TEST_P(PalindromeCheckerTest, ChecksForInvalidPalindromes) {
    std::string test_string = GetParam();
    EXPECT_FALSE(isPalindrome(test_string)) << "Expected \"" << test_string << "\" not to be a palindrome.";
}

INSTANTIATE_TEST_SUITE_P(
    NonPalindromeValues,
    PalindromeCheckerTest,
    testing::Values("hello", "world", "test", "openai")
);


int main(int argc, char **argv) {
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

위 코드에서 INSTANTIATE_TEST_SUITE_P는 지정된 파라미터 값 각각에 대해 PalindromeCheckerTest의 테스트 케이스들을 생성하고 실행합니다. 예를 들어, "madam"이라는 파라미터로 ChecksForValidPalindromes 테스트가 한 번 실행되고, 이어서 "racecar"로 또 한 번 실행되는 식입니다.

태그: GoogleTest C++ unit testing Test Framework cmake

7월 28일 10:30에 게시됨