本記事ではC++の単体テストフレームワークであるGoogle Testを、CMakeを使ってプログラムにリンクできるようにするための方法を紹介します。 Google Testを毎回手動でダウンロードするのは面倒ですが、本記事で紹介する方法ではCMake内でGoogle Testをgit submoduleで管理できます。

目次

なぜCMakeなのか

CMakeを使うメリットはこちらの記事にたくさん書かれていますが、一番のメリットはクロスプラットフォームであるという点だと思います。Windows、LinuxなどのOS依存な設定を書かずに済むというのは便利です。

なぜGoogle Testなのか

Google Testの公式ドキュメント 1 で述べられている思想を以下にまとめます。

  • テストを独立に実行できる… あるテストが失敗しても、他のテストは実行される
  • テストをグループ化できる… 類似するテストはデータや関数を共有できる
  • 異なるOSでも実行できる… プッラットフォームに依存しないのでさまざまな環境でテストできる
  • 定義したテストをユーザが管理しなくても良い… RUN_ALL_TESTS()を実行すれば定義されたユニットテストがすべて実行される
  • テストが速い… set-up/tear-downは各テストで依存しないようにしつつ、一度しか実行しないようにする

ディレクトリ構造

本記事ではディレクトリ構造を以下のようにします。 トップディレクトリにCMakeLists.txtsrcthird_partyがあります。 自分で作成してプログラムをsrc以下に、今回利用するユニットテストフレームワークであるGoogle Testはthird_party以下に配置するようにします。

.
├── CMakeLists.txt
├── src
│   ├── CMakeLists.txt
│   └── something_test.cc
│   └── test_harness.cc
└── third_party
    ├── googletest
    └── CMakeLists.txt

googletestのサブモジュール化

GitHub上で公開されているgoogletestをダウンロードし、submoduleとして追加します。 本記事ではダウンロードしたgoogletestをthird_party以下で管理することとします。

mkdir third_party
git submodule add https://github.com/google/googletest.git third_party/googletest

CMakeLists.txtの設定

CMakeLists.txtでは必要に応じてgit sumoduleを利用してGoogle Testをダウンロードするような設定を記述します。 この設定があることで、ユーザは-DGOOGLETEST=onオプションを付けてcamkeを実行することで、submoduleをダウンロードします2。 git submodule関連のコマンドをCMakeLists.txtに記述してあるので、このプログラムを利用するユーザはgit submoduleの知識は不要になります。 つまり、このプログラムのインストール手順が簡単になります。

cmake_minimum_required(VERSION 3.16)

find_package(Git QUIET)

# Gitがインストールされており、トップディレクトリに.gitディレクトリがある
if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git")
    enable_testing()
    option(GOOGLETEST "Check Google Test during build" ON)
    # -DGOOGLETEST=onオプションを付けて実行したらsubmoduleを最新版にする
    if(GOOGLETEST)
        message(STATUS "Submodule update")
        execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive
                        WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
                        RESULT_VARIABLE GIT_SUBMOD_RESULT)
        if(NOT GIT_SUBMOD_RESULT EQUAL "0")
            message(FATAL_ERROR "git submodule update --init failed with ${GIT_SUBMOD_RESULT}, please checkout submodules")
        endif()
    endif()
endif()

if(NOT EXISTS "${PROJECT_SOURCE_DIR}/third_party/googletest/CMakeLists.txt")
message(FATAL_ERROR "The submodules were not downloaded! GOOGLETEST was turned off or failed. Please update submodules and try again.")
endif()


add_subdirectory(src)
add_subdirectory(third_party)

またthird_party/CMakeLists.txtでは以下のように実行ファイルを作成し、Google Testをリンクします。

add_subdirectory(googletest)

add_executable(test_harness ../src/test_harness.cc ../src/something_test.cc)

target_include_directories(
	test_harness PUBLIC
	${PROJECT_SOURCE_DIR}/third_party/googletest/googletest/include
)

target_link_libraries(
	test_harness
	gtest
)

add_test(NAME test_harness COMMAND test_harness)

本記事では以下のようなテストプログラムを作成しています。

src/something_test.cc

#include "gtest/gtest.h"

class SomethingTest : public ::testing::Test {
  protected:
    void SetUp() override {
      x_ = 5;
    }

  int x_;

};


TEST_F(SomethingTest, Add) {
  EXPECT_EQ(10, x_ + 5);
}

// 失敗するテスト
TEST_F(SomethingTest, Add2) {
  EXPECT_EQ(10, x_ + 6);
}

src/test_harness.cc

#include "gtest/gtest.h"


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

RUN_ALL_TESTS()によって、定義されたすべてのユニットテストが実行されます。 上記で準備が終わったらコンパイルして、ユニットテストの実行ファイルを作成し、実際にテストを実施します。

mkdir build
cd build
cmake -DGOOGLETEST=on ../
./third_party/test_harness

実行結果は以下のようになります。

[==========] Running 2 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 2 tests from SomethingTest
[ RUN      ] SomethingTest.Add
[       OK ] SomethingTest.Add (0 ms)
[ RUN      ] SomethingTest.Add2
gtest-build/src/something_test.cc:19: Failure
Expected equality of these values:
  10
  x_ + 6
    Which is: 11
[  FAILED  ] SomethingTest.Add2 (0 ms)
[----------] 2 tests from SomethingTest (0 ms total)

[----------] Global test environment tear-down
[==========] 2 tests from 1 test suite ran. (0 ms total)
[  PASSED  ] 1 test.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] SomethingTest.Add2

 1 FAILED TEST

2つのテストのうち1つは失敗していることがわかります。

おわり

本記事ではCMakeでGoogle Testを利用するための手順を紹介しました。 Google Testはgitのsubmoduleで管理し、submoduleの更新をCMake内で完結することも紹介しました。 ユニットテストフレームワークを簡単に導入できることで、ユニットテストを書くまでの心理的な障壁が少しでも下がるのはいいことですよね。


  1. https://github.com/google/googletest/blob/master/googletest/docs/primer.md ↩︎

  2. https://cliutils.gitlab.io/modern-cmake/chapters/projects/submodule.html を参考にしました。 ↩︎


関連記事





最近の記事