【C++】CMakeでGoogle Testをリンクできるようにする
本記事では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.txt
、src
、third_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内で完結することも紹介しました。 ユニットテストフレームワークを簡単に導入できることで、ユニットテストを書くまでの心理的な障壁が少しでも下がるのはいいことですよね。