본문 바로가기

개발/C++

C++ 에서 Google Test 사용

요구사항


  • CMake 설치
  • C++ 14 이상

프로젝트 세팅


CMake 는 CMakeLists.txt 를 통해 프로젝트의 빌드 시스템을 설정 합니다. 그렇기 때문에 해당 파일에 GoogleTest와의 의존성을 선언해줘야 합니다.

튜토리얼은 Windows 10, VS Code에서 진행 합니다.

먼저, 튜토리얼을 위한 폴더를 만들고, VS Code에서 열어줍니다.

다음으로, CMakeLists.txt 파일을 생성하고 GoogleTest 의존성을 추가해줍니다.

GoogleTest의 의존성을 추가하는 방법은 여러가지지만, 해당 예제에서는 CMake 모듈인 FetchContent를 통해 추가하도록 하겠습니다.

FetchContent란 ?

외부 프로젝트 모듈을 통해 content를 구성할 수 있게 해주는 CMake 모듈입니다. 구성 단계에서 콘텐츠를 include() 와 같은 명령에 사용할 수 있도록 해줍니다.

cmake_minimum_required(VERSION 3.14)
project(my_project)

# GoogleTest requires at least C++14
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

include(FetchContent)
FetchContent_Declare(
  googletest
  URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip
)
# For Windows: Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)

위 URL 에서 03597a01ee50ed33e9dfd640b249b4be3799d395와 같은 hash를 통해 version을 변경할 수 있습니다.

테스트 케이스 개념


테스트 케이스에 들어가기 전에, assertion에 대해 알아보겠습니다.

Assertions

구글 테스트에서 assertions는 함수 호출과 유사한 매크로입니다. 클래스나 함수를 assertions를 통해 테스트 할 수 있습니다. assertion이 실패하면, 테스트 failture에 관한 정보를 google test가 표시해 줍니다. assertion에 관한 자세한 내용은 여기에서 확인할 수 있습니다.

  • EXPECT_* → 실패해도 실행하던 테스트를 이어 합니다.
    EXPECT_EQ(x[i], y[i]) << "Vectors x and y differ at index " << i`
  • ASSERT_* → 실패하면 테스트를 종료 합니다.
    ASSERT\_EQ(x.size(), y.size()) << "Vectors x and y are of unequal length";

테스트 케이스는 아래 두 가지 방법을 통해 만들 수 있습니다.

Test(TestSuiteName, TestName)

Test()함수는 값을 return 하지 않은 C++ 함수입니다. 해당 함수 내에서 유효한 C++ statements들과 함께, assertions를 통해 값을 체크하면 됩니다. Test는 값을 return 하지 않기 때문에 result는 assertions에 의해 결정 됩니다. assertion에서 실패가 발생하거나 테스트에 crash 등이 발생하면 전체 test가 실패인 것으로 간주 됩니다. Test함수의 argument들은 유효한 C++ 식별자여야 하고, _ 가 포함되면 안됩니다.

Test_F(TestFixtureClassName, TestName)

Test_F() 함수는 비슷한 data에 대해서 두 개 이상의 테스트를 작성해야 할 때 사용하는 함수입니다. Test_F를 통해 똑같은 configuration을 여러 개의 테스트에서 재사용 할 수 있게 해줍니다.

해당 기능은 아래 절차를 통해 사용할 수 있습니다.

  1. testing::Test를 상속 받습니다.
  2. class 내부에 사용하고자 계획한 객체들을 선언해 줍니다.
  3. 필요하다면 기본 생성자나 SetUp() 함수를 통해서 각 테스트를 위한 객체를 세팅 합니다. (대소문자에 유의할 것)
  4. Setup()에서 리소스를 할당해 줬다면, TearDown()을 이나 소멸자를 통해 해제 합니다.

테스트 케이스 작성 및 실행


Hello Test

먼저 hello_test.cpp파일을 작성해 줍니다.

#include <gtest/gtest.h>
 
// Demonstrate some basic assertions.
TEST(HelloTest, BasicAssertions) {
  // Expect two strings not to be equal.
  EXPECT_STRNE("hello", "world");
  // Expect equality.
  EXPECT_EQ(7 * 6, 42);
}

EXPECT_STRNE(str1, str2) 는 str1 과 str2가 다르면 성공하는 assertion이고, EXPECT_EQ(val1, val2)는 val1 과 va2가 같으면 성공하는 assertion입니다.

다음으로 CMakeLists.txt에 아래 내용을 추가해줍니다.

enable_testing()
 
add_executable(
  hello_test
  hello_test.cpp
)
target_link_libraries(
  hello_test
  GTest::gtest_main
)
 
include(GoogleTest)
gtest_discover_tests(hello_test)

그리고 아래 명령어를 통해 build 후 실행해줍니다.

cmake -S . -B build
cmake --build build
 
cd build && ctest

마지막 명령어를 실행했을 때 결과 입니다.

테스트 목록과 실행 결과에 대해 위와 같이 알려줍니다.

 

 

위와 같이 터미널을 통해 결과를 확인해도 되지만, VS Code에서는 테스트 리스트업 및 실행 결과를 좀 더 직관적으로 표시해주는 extension이 존재 합니다.

vscode extension 탭에서 C++ TestMate를 설치해줍니다. 그리고 CMake를 통해 Build를 진행해주면, Testing 탭에서 작성한 테스트 목록과 실행 결과를 확인할 수 있습니다.

Assertion Test

assertion에서 ASSERT_* 과 EXPECT_*의 차이를 알 수 있는 테스트도 작성해 보겠습니다.

assert_test.cpp에 아래와 같은 테스트 코드를 작성해 줍니다.

#include <gtest/gtest.h>
 
TEST(AssertTest, AssertHandle) {
  // Expect two strings not to be equal.
  ASSERT_STRNE("hello", "hello");
  // Expect equality.
  EXPECT_EQ(7 * 6, 41);
}
 
TEST(AssertTest, ExpectHandle) {
  // Expect two strings not to be equal.
  EXPECT_STRNE("hello", "hello");
  // Expect equality.
  EXPECT_EQ(7 * 6, 41);
}

그리고 CMakeLists.txt에도 해당 cpp파일을 추가해 줍니다.

위 두 가지 테스트에서 다른점은 한가지 입니다. STRNE에 대해 ASSERT와 EXPECT의 차이 입니다. 빌드하고 테스트를 실행 해보겠습니다.

실행 결과에서 확인할 수 있듯이, ASSERT_STRNE을 사용한 실패 시 테스트가 바로 종료 되어 아래 테스트가 실패하더라도 결과를 알 수 없는 반면, EXPECT_STRNE를 사용하면 테스트가 실패하더라도 아래 테스트까지 모두 실행하는 것을 확인할 수 있습니다.

 

그렇기 때문에, 심각한 오류 일 때는 ASSERT를 사용하고, 아닐 때는 EXPECT를 사용하여 목적에 맞게 구분해서 사용하면 좋을 것 같습니다.

Fixture Test

먼저 fixture_test.cpp를 만들고 테스트 시 사용할 Queue class와 QueueTest class, 마지막으로 테스트 케이스 까지 만들겠습니다.

#include <gtest/gtest.h>
 
template <typename E>
class Queue {
 private:
  size_t size_;
  E* data_;
  size_t head_;
  size_t tail_;
  size_t count_;
 
 public:
  Queue(int size) {
    size_ = size;
    data_ = new E[size_];
    head_ = 0;
    tail_ = 0;
    count_ = 0;
  }
  ~Queue() {
    delete[] data_;
  }
 
  void Enqueue(const E& elem) {
    if (count_ == size_) {
      throw std::out_of_range("Queue is full");
    }
    data_[tail_] = elem;
    tail_ = (tail_ + 1) % size_;
    ++count_;
  }
 
  E Dequeue() {
    if (count_ == 0) {
      throw std::out_of_range("Queue is empty");
    }
    E ret = data_[head_];
    head_ = (head_ + 1) % size_;
    --count_;
    return ret;
  }
 
  size_t size() { return count_; }
 
  bool isEmpty() { return count_ == 0; }
};
 
class QueueTest : public testing::Test {
 protected:
  QueueTest() { printf("QueueTest\n"); }
  ~QueueTest() { printf("~QueueTest\n"); }
  void SetUp() override {
    printf("SetUp\n");
    q0_ = new Queue<int>(10);
    q1_ = new Queue<int>(5);
    q2_ = new Queue<int>(1);
 
    q1_->Enqueue(1);
    q2_->Enqueue(2);
  }
 
  void TearDown() override {
    printf("TearDown\n");
    delete q0_;
    delete q1_;
    delete q2_;
  }
 
  Queue<int>* q0_;
  Queue<int>* q1_;
  Queue<int>* q2_;
};
 
TEST_F(QueueTest, IsEmptyInitially) {
  printf("IsEmptyInitially\n");
  EXPECT_EQ(q0_->size(), 0);
}
 
TEST_F(QueueTest, ExceptionWhenDequeueFromEmptyQueue) {
  printf("ExceptionWhenDequeueFromEmptyQueue\n");
  EXPECT_EQ(q0_->size(), 0);
  EXPECT_EQ(q0_->isEmpty(), true);
  EXPECT_ANY_THROW(q0_->Dequeue());
}

 

TEST_F 실행 과정을 알아보기 위해 SetUp과 TearDown에 출력문도 추가 하고 테스트를 실행해 보겠습니다.

위 실행 결과를 통해 TEST_F는 다음과 같은 절차를 통해 실행 된다는 것을 확인할 수 있습니다.

  1. QueueTest 객체 생성
  2. SetUp을 통한 data 세팅
  3. TEST_F실행
  4. TearDown을 통한 메모리 release
  5. QueueTest 객체 소멸

또한, 각 테스트마다 위와 같은 과정을 반복함으로 항상 일관된 data를 사용할 수 있습니다.

'개발 > C++' 카테고리의 다른 글

QNX 에서 Google Test 돌리기  (0) 2023.12.22
C++ RAII  (0) 2023.12.19