今回やること
単体テストgoogletestの導入
googletestとは
Googleが提供するC++用の単体テストフレームワーク
参考: Google Test の使い方 - ゼロから学ぶ C++
python版のpytestと提供される機能はほとんど同じですね.
googletestの導入
現状のディレクトリ構成
GitHub - nariaki3551/master_mind_cpp at feature/action_cmake
master_mind_cpp $ tree ./ ./ ├── README.md ├── CMakeLists.txt └── src ├── CMakeLists.txt ├── def.h ├── main.cpp ├── ... └── utils.h
googletestのインストール
githubからcloneします.
$ git clone https://github.com/google/googletest $ cd googletest $ mkdir build; cd build; cmake ..; make; make install
ubuntuの場合は
$ apt install libgtest-dev
でもok.
cmakeを用いる場合, 事前にインストールしていなくても
include(FetchContent) FetchContent_Declare( googletest URL https://github.com/google/googletest/archive/refs/tags/release-1.11.0.zip ) set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) FetchContent_MakeAvailable(googletest)
にてコンパイル時に自動でインストールしてくれる. 便利. ( Quickstart: Building with CMake | GoogleTest )
テストプログラム
utils.hの中のcountHitBlow関数のテストを作成したいと思います. 2つのコードが与えられたときに, そのコードの組みのヒント(hit, blow)を返す関数です.
test/test.cpp
を作成し, そこに下記のようにテストコードを書きます.
ASSERT_EQ で, 関数の返り値と期待する値が等しいかどうかを3通りの入力で確かめています.
#include <gtest/gtest.h> #include "utils.h" TEST ( countHitBlowTest, countHitBlowTest1 ) { Code codeA{ 0, 0 }; Code codeB{ 0, 0 }; ASSERT_EQ( countHitBlow(codeA, codeB), HitBlow(2, 0) ); } TEST ( countHitBlowTest, countHitBlowTest2 ) { Code codeA{ 0, 0 }; Code codeB{ 0, 1 }; ASSERT_EQ( countHitBlow(codeA, codeB), HitBlow(1, 0) ); } TEST ( countHitBlowTest, countHitBlowTest3 ) { Code codeA{ 0, 0 }; Code codeB{ 1, 1 }; ASSERT_EQ( countHitBlow(codeA, codeB), HitBlow(0, 0) ); }
ここ 入門ガイド — Google Test ドキュメント日本語訳 を見れば, 他にどのようなテスト用の比較関数があるか確認できます.
ビルド
googletestをインストールしている場合
コマンドラインでビルドする際は, -lgtest, -lgtest_main をリンクさせます.
$ g++ ./test/test.cpp -std=c++17 -I./src -lgtest -lgtest_main
cmakeであれば
add_executable( utest test.cpp ) target_include_directories( utest PRIVATE ${PROJECT_SOURCE_DIR}/src ) target_compile_features( utest PRIVATE cxx_std_17 ) target_link_libraries( utest PRIVATE gtest # 追加!! gtest_main # 追加!! pthread # 追加!! )
と, 編集してルートディレクトリのCmakeLists.txtを
cmake_minimum_required(VERSION 3.1) project(MasterMind CXX) # setting of CMAKE_RUNTIME_OUTPUT_DIRECTORY if(NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin) endif() add_subdirectory(src) add_subdirectory(utest) # 追加!!
としてやれば, あとは
$ mkdir build; cd build; cmake ..; make
でコンパイルされます. バイナリは ./bin/test
に作成されてあります.
作業後のディレクトリ構成
. ├── CMakeLists.txt ├── README.md ├── src │ ├── CMakeLists.txt │ ├── def.h │ ├── main.cpp │ ├── ... │ └── utils.h └── test # 追加!! ├── CMakeLists.txt # 追加!! └── test.cpp # 追加!!
googletestをインストールしていない場合
find_package(GTest) # googletest packageを検索 if(NOT GTest_FOUND) # 無ければFetchContent_Declearを用いてダウンロード include(FetchContent) FetchContent_Declare( googletest URL https://github.com/google/googletest/archive/refs/tags/release-1.11.0.zip ) set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) FetchContent_MakeAvailable(googletest) endif() add_executable( utest test.cpp ) ..(以下略)..
でも入ります. 便利. ( Quickstart: Building with CMake | GoogleTest )
実行
Running main() from /xxx/master_mind_cpp_dev/source/googletest/googletest/src/gtest_main.cc [==========] Running 3 tests from 1 test suite. [----------] Global test environment set-up. [----------] 3 tests from countHitBlowTest [ RUN ] countHitBlowTest.countHitBlowTest1 [ OK ] countHitBlowTest.countHitBlowTest1 (0 ms) [ RUN ] countHitBlowTest.countHitBlowTest2 [ OK ] countHitBlowTest.countHitBlowTest2 (0 ms) [ RUN ] countHitBlowTest.countHitBlowTest3 [ OK ] countHitBlowTest.countHitBlowTest3 (0 ms) [----------] 3 tests from countHitBlowTest (0 ms total) [----------] Global test environment tear-down [==========] 3 tests from 1 test suite ran. (0 ms total) [ PASSED ] 3 tests.
動いていますね.
ctest
ついでにctestも設定します. CMakeLists.txtにenable_testing()
以下を追加します.
cmake_minimum_required(VERSION 3.1) project(MasterMind CXX) # ( 中略 ) add_subdirectory(src) add_subdirectory(utest) # test # 追加!! enable_testing() add_test( NAME utest COMMAND utest )
これにより,
$ mkdir build; cd build; cmake ..; make
のbash後に
$ ctest
でgoogletestが動きます.
Test project /xxx/master_mind_cpp/build Start 1: ugtest 1/1 Test #1: ugtest ........................... Passed 0.00 sec 100% tests passed, 0 tests failed out of 1 Total Test time (real) = 0.01 sec
GitHub action の修正
最後に github actionを修正します. gtestがないとエラーが起きています.
Run cmake --build /home/runner/work/master_mind_cpp/master_mind_cpp/build --config Release ( 中略 ) 10 /home/runner/work/master_mind_cpp/master_mind_cpp/test/test.cpp:1:10: fatal error: gtest/gtest.h: No such file or directory 11 1 | #include <gtest/gtest.h> 12 | ^~~~~~~~~~~~~~~ 13 compilation terminated. ( 中略 ) 17 Error: Process completed with exit code 2.
修正.. introduce gtest by nariaki3551 · Pull Request #6 · nariaki3551/master_mind_cpp · GitHub
次はpthreadに関するエラー.
( 中略 ) 10 [100%] Linking CXX executable ../../bin/utest 11 /usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/libgtest.a(gtest-all.cc.o): in function `testing::internal::ThreadLocal<std::vector<testing::internal::TraceInfo, std::allocator<testing::internal::TraceInfo> > >::~ThreadLocal()': 12 ( 中略 ) 44 ./obj-x86_64-linux-gnu/googletest/./googletest/include/gtest/internal/gtest-port.h:1787: undefined reference to `pthread_getspecific' 45 /usr/bin/ld: ./obj-x86_64-linux-gnu/googletest/./googletest/include/gtest/internal/gtest-port.h:1794: undefined reference to `pthread_setspecific' 46 collect2: error: ld returned 1 exit status ( 中略 ) 50 Error: Process completed with exit code 2.
修正.. introduce gtest by nariaki3551 · Pull Request #6 · nariaki3551/master_mind_cpp · GitHub
これでclearになりました.
まとめ
googletestを追加して, cmakeが動くように, それに合わせてCMakeLists.txtを変更しました.
コード
参考
他の記事
- 次の記事
- 前の記事
- 一覧 mastermind カテゴリーの記事一覧 - サブロウ丸