C++ のユニットテストをいい感じにする


C++ のユニットテストを簡単にあつかえるようにしてみた。
要素としては次。

  • Google Test
  • CMake
    • http://www.cmake.org/
    • ユニットテストのコンパイルを楽にするために。
    • 実行と結果の要約表示は付属の CTest を使うといい感じになる。

ここでは CMake まわりの設定について説明していく。
Google Test の使い方については各自で調べてください。

サンプル

CMake の都合上ディレクトリー構造がキモになってくるのでわかりやすさのためにサンプル作ってみた。

トップで次のコマンドを打つとテストまで完了する。

mkdir build
cd build
cmake ..
make
ctest

ディレクトリー構造は次のようにしている。


project root/
    cmake/                  # CMake 用設定置き場
    include/                # C++ 用ヘッダーファイル置き場
    lib/                    # ライブラリー用ディレクトリー
        CMakeLists.txt      # ライブラリー用設定ファイル
        util/               # ライブラリー そのいち
        ...                 # その他のライブラリー
    test/                   # テスト用ディレクトリー
        CMakeLists.txt      # テスト用設定ファイル
        awesome/            # テスト そのいち
        ...                 # その他の機能群用のディレクトリー
    CMakeLists.txt          # ビルド用設定ファイル
    main.cpp                # 実行ファイル用ソース

トップの CMakeLists.txt の次の部分がテストに関する設定。

# tests
if(NOT without-test)
    enable_testing()
    include(cmake/gtest.cmake)
    add_subdirectory(test)
endif()

CMake の条件分岐

ここではテストのコンパイルをやめるかどうかを制御するために使っている。普段はテストこみでコンパイル、事情があるときだけ外す、という形。

make 時に変数 without-test が定義されているかどうかで判別している。
条件分岐については次参照。

CTest を有効化する

enable_testing() で有効になる。

その上で次のコマンドでテストの設定をする。

  • add_test()
    • ctest コマンド実行時にテスト対象に含める実行ファイルを指定する。
    • add_executable の TARGET_FILE を指定しておいたりする。
  • set_property()
    • テストにラベルをつけて、特定のラベルのついたテストのみ実行ということができる。
    • set_tests_properties() でもつけられるけど紛らわしいのでおすすめしない。
cmake_minimum_required(VERSION 2.8)

add_executable(awesome-test main.cpp)
target_link_libraries(awesome-test
    gtest
    gtest_main
    pthread
    util
    )

add_test(
    NAME awesome
    COMMAND $<TARGET_FILE:awesome-test>
    )

# run with: ctest -L lib
set_property(
    TEST awesome
    PROPERTY LABELS lib awesome
    )

上記のようにしておくと次のコマンドのいずれかでこのテストが走る。
make test はラベル指定時が煩雑だね。

  • ctest
  • ctest -L lib
  • make test
  • ARGS="-L lib" make test

CMake で外部プロジェクトを取り込む

CMake には ExternalProject というモジュールが付属していて、リモートの CMake プロジェクトを取り込めるようになっている。

Google Test は ExternalProject で読み込めるようになっている。
次のような設定をファイルに切り出しておくと使い回しがきく。

gtest.cmake
cmake_minimum_required(VERSION 2.8)

# Google Test settings
include(ExternalProject)

ExternalProject_Add(
    GoogleTest
    URL https://googletest.googlecode.com/files/gtest-1.7.0.zip
    PREFIX ${CMAKE_CURRENT_BINARY_DIR}/lib
    INSTALL_COMMAND ""
    LOG_DOWNLOAD ON
    )

ExternalProject_Get_Property(GoogleTest source_dir)
include_directories(${source_dir}/include)

ExternalProject_Get_Property(GoogleTest binary_dir)
add_library(gtest STATIC IMPORTED)
set_property(
    TARGET gtest
    PROPERTY IMPORTED_LOCATION ${binary_dir}/libgtest.a
    )
add_library(gtest_main STATIC IMPORTED)
set_property(
    TARGET gtest_main
    PROPERTY IMPORTED_LOCATION ${binary_dir}/libgtest_main.a
    )

実際に使う場合は include(gtest.cmake) という感じで。

テストを作る場合

test ディレクトリー以下に追加していく。
機能や関数ごとにサブディレクトリーを作って追加していくのがいい。
サブディレクトリーを追加した場合は test/CMakeLists.txt にそのディレクトリーを add_subdirectory() するのを忘れないように。

あと include ディレクトリーを include_directories() しているので、ヘッダーファイルのパスを include 直下からのものにする。