はじめに
CMakeで構成されたC++のプロジェクトに、googletestによるユニットテストを組み込む方法です。
2020年末の自身の記事「CMakeプロジェクトでgoogletest」のリンクを手繰ってみたら公式の手順("Incorporating Into An Existing CMake Project")が結構変わっていたので、それへの対応版です。
この方法のメリット
- OSが持つgoogletestライブラリに依存しないので、
- (yumやaptのための)管理者権限が不要です。
- 誰が、どこで(どのOSで)実行しても、安定した結果が得られます。
- ビルドの実行ごとにgoogletestのソースコードをcloneするので、
- 最新のgoogletestを利用できます。
- ソースリポジトリを汚さず、必要最小限のものだけでプロジェクトを構成できます。
手順
手順0: 普通のCMakeプロジェクトを作成
とりあえず既にあるという前提です。
$ tree
.
├── CMakeLists.txt
└── src
├── factorial.cpp
├── factorial.h
└── main.cpp
cmake_minimum_required(VERSION 3.14)
project(example_googletest_on_cmake)
set(CMAKE_CXX_STANDARD 14)
add_executable(main src/main.cpp src/factorial.cpp)
この方法はFetchContent_MakeAvailable()
コマンドを使うため、CMake3.14以上が必要です。それより古いCMakeを使う場合は、前回の記事「CMakeプロジェクトでgoogletest」を参照してください。
main.cpp
やfactorial.{cpp,h}
の内容は省きます。ファイル名から適当にお察しください。
今回はfactorial.cpp
がテストしたいファイルになります。
手順1: 既存のCMakeLists.txt
に設定を追加
既存のCMakeLists.txt
の末尾に、
https://github.com/google/googletest/tree/main/googletest#build-with-cmake
に記載された以下の内容を追記してください。
ただし、最後の数行は既存プロジェクトに合わせた書き換えが必要になります。詳しくは次の手順で説明します。
include(FetchContent)
FetchContent_Declare(
googletest
# Specify the commit you depend on and update it regularly.
URL https://github.com/google/googletest/archive/609281088cfefc76f9d0ce82e1ff6c30cc3591e5.zip
)
# For Windows: Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)
# Now simply link against gtest or gtest_main as needed. Eg
add_executable(example example.cpp)
target_link_libraries(example gtest_main)
add_test(NAME example_test COMMAND example)
なお、609281088cfefc76f9d0ce82e1ff6c30cc3591e5
は適宜、最新のコミットidに書き換える必要があります。
手順2: CMakeLists.txt
をプロジェクトに合わせて書き換え
手順1で追記したgoogletestの設定内容を、現在のプロジェクトに合わせて書き換えます。
書き換えが必要なのは最後の3行だけです。
# Now simply link against gtest or gtest_main as needed. Eg
add_executable(example example.cpp)
target_link_libraries(example gtest_main)
add_test(NAME example_test COMMAND example)
add_executable(${PROJECT_NAME}-googletest src/factorial.cpp test/test_factorial.cpp)
target_link_libraries(${PROJECT_NAME}-googletest gtest_main)
target_include_directories(${PROJECT_NAME}-googletest PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src)
add_test(NAME test COMMAND ${PROJECT_NAME}-googletest)
enable_testing()
add_executable()
- https://cmake.org/cmake/help/latest/command/add_executable.html
- 生成するgoogletestの実行ファイル(
${PROJECT_NAME}-googletest
)と、
リンクするファイル(テストコードとテスト対象のコードを含むファイル)を列挙します。 - 実行ファイル名は変更せず
example
のままでも大丈夫ですが、、、まぁ普通にexample
は無いですよね。 - テストコードはtestディレクトリに配置(
test/test_factorial.cpp
)するようにしています。 -
main()
関数はgoogletestに含まれる方を使いますので、
実行ファイルをビルドするようなプロジェクトの場合、main()
を含むファイルは除外してください。 - 言うまでもありませんが、
実際のプロジェクトでは大量のファイル名を何度も記述することになるので、変数に格納して使い回すのが定石です。
target_link_libraries()
- https://cmake.org/cmake/help/latest/command/target_link_libraries.html
- googletestの
main()
を含むライブラリgtest_main
をリンクします。 - ターゲットファイル名は
add_executable()
の記述に合わせて書き換えてください。
target_include_directories()
- https://cmake.org/cmake/help/latest/command/target_include_directories.html
- 通常、テストコードの中でテスト対象の関数を呼び出すには、それを宣言したヘッダファイルをincludeする必要があります。
ですので、ヘッダファイルが配置してあるディレクトリを記述してください。 - この例は、全てのヘッダファイルがsrcディレクトリに配置してあるという仮定に基づいた設定です。
ライブラリをビルドするプロジェクトの場合、公開するAPIについては個別にincludeディレクトリなどに置くケースもあるはずです。
その場合は適宜記述を変更してください。 - ターゲットファイル名は
add_executable()
の記述に合わせて書き換えてください。
add_test()
- https://cmake.org/cmake/help/latest/command/add_test.html
-
ctest
経由で実行されるテストを記述します。 -
COMMAND
にはadd_executable()
に記述したターゲットファイル名を指定してください。 -
NAME
は、複数のテストを構成する場合には、
個別にユニークな名前を指定する必要があるそうですが、今回は1個だけなので何でも良いです。
(ドキュメントに記述が見つかりませんでしたが、
add_test()
の記述が1個だけの場合はNAMEの宣言すら必要ありませんでした。) -
ctest
を使わずコマンドラインから直接実行してもテスト可能なので、add_test()
は無くても構いません。
enable_testing()
- https://cmake.org/cmake/help/latest/command/enable_testing.html
-
ctest
でテストを実行する場合にはadd_test()
と合わせて宣言します。 - 他にも、生成される
Makefile
にtestというルールを追加する機能もあるようです。
手順3: テストを書く&実行する
本題(CMakeプロジェクトへのgoogletestの組み込み方法)とは別の話なので、簡単に。
テストコード
googletestの使い方は「Google Test ドキュメント日本語訳」で丁寧に説明されています。
「上級ガイド」や「よくある質問」も役に立ちそうな情報がてんこ盛りなので、一通り目を通しておきましょう。
#include <stdexcept>
#include "gtest/gtest.h"
#include "factorial.h"
TEST(test_factorial, positive_values) {
EXPECT_EQ(1, factorial(1));
EXPECT_EQ(2, factorial(2));
EXPECT_EQ(6, factorial(3));
}
TEST(test_factorial, zero) {
EXPECT_EQ(1, factorial(0));
}
TEST(test_factorial, negative_value) {
EXPECT_THROW(factorial(-1), std::invalid_argument);
}
ビルド
普通にCMakeのビルドと同じです。
$ mkdir build
$ cd build
$ cmake ..
$ make all
テスト実行
add_executable()
に記述したターゲットファイル名を実行するだけです。
add_test()
とenable_testing()
を記述してある場合は、ctest
やmake test
でも実行可能です。
「テストプログラムを実行する:高度なオプション」も参考にしてください。
$ ./example_googletest_on_cmake-googletest
Running main() from /any/where/example-googletest-with-cmake/build/_deps/googletest-src/googletest/src/gtest_main.cc
[==========] Running 3 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 3 tests from test_factorial
[ RUN ] test_factorial.positive_values
[ OK ] test_factorial.positive_values (0 ms)
[ RUN ] test_factorial.zero
[ OK ] test_factorial.zero (0 ms)
[ RUN ] test_factorial.negative_value
[ OK ] test_factorial.negative_value (0 ms)
[----------] 3 tests from test_factorial (1 ms total)
[----------] Global test environment tear-down
[==========] 3 tests from 1 test suite ran. (2 ms total)
[ PASSED ] 3 tests.
おわりに
前回の内容と比べると手順も減ったので、普段からCMakeを使っている人には簡単すぎる内容だったと思います。
つまり最初からCMakeで運用していれば、ユニットテストを組み込むのは「簡単」だと言うことです。
Makefile
を駆使した謎ビルドシステムを構築したり、build.sh
でお茶を濁したりせず、定番(=CMake)を導入した方が結果的に低コストに運用できると思います。