単体テストの効率化を考える(19)
単体テストのビルドを自動化してみた
(cmakeとVisualStudioの場合)

2022年12月7日

どもです。

今回は、作成した単体テストのコードをビルドしてテストを実行する、というプロセスを自動化してみたので、それについて書きます。

単体テストの効率化を考える(11)で、自動で生成したテストコードのVisualStudioでビルドと実行は試していました。
この時は、VisualStudioの起動や設定、ビルドは全て「手動」で行っていました。
テストのプロジェクト/ソリューションが1つだけならよいのですが、複数の場合にはそれなりに手間になります。
そこで今回は、この「手動」の部分を「自動化」することで、単体テストの効率化を図ってみました。

1. 単体テストの環境

今回のエントリでは、以下の環境で作業を行います。

単体テストの実行環境
項目 内容
OS Windows10 Pro(22H2)
CPU i7-8700
メモリ 16GB
IDE Visual Studio Commnuity 2022
version 17.3.6
テストフレームワーク google test ver.1.12.0
cmake ver.3.25.0

また今回の作業は、以下のフォルダ構成で行っています。

E:\tmp\cmake_autotest_sample
│  tree.txt
│  
├─src
│      sample_src.cpp	<- テスト対象関数のソースコード
│      
└─test
	├─framework
	│  ├─include
	│  │  └─gtest
	│  ├─x64
	│  └─x86
	└─utest
		└─sample_function_001_test
			│  CMakeLists.txt
			│  
			└─driver
					sample_function_001_test.cpp	<- テストドライバ
					sample_function_001_test.h

2. cmakeの設定

まず、cmakeの設定です。
cmakeで使用するCMakeLists.txtは、以下のように設定します。

cmake_minimum_required (VERSION 3.8)

project (sample_function_001_test)

set (GTEST_ROOT E:/tmp/cmake_autotest_sample/test/framework)
set (TEST_ROOT ./ )
set (CMAKE_BUILD_TYPE Debug)

include_directories (${GTEST_ROOT}/include/)
include_directories (${TEST_ROOT}/driver/)

add_executable(${PROJECT_NAME} 
	driver/sample_function_001_test.cpp 
	E:/tmp/cmake_autotest_sample/src/sample_src.cpp
	)

target_link_directories(${PROJECT_NAME}
	PUBLIC ${GTEST_ROOT}/x64/Debug
	)

target_link_libraries(${PROJECT_NAME}
	gtestd
	gtest_maind
	)

CMakeLists.txtの内容を少し解説します。

まず、add_executableの設定です。
テスト対象関数が実装されたファイルのパスは、絶対パスで指定しています。
テスト対象関数のソースファイルがある場所にCMakeLists.txtが格納されていた場合、ビルドに影響を与える可能性が考えられます。
これを回避するために、テスト対象関数のソースファイルは絶対パスで指定しています。

テストドライバは、CMakeLists.txtが格納されたフォルダよりも下のフォルダに格納されていますので、そのファイルへの相対パスで指定しています。
テストのビルドに必要なファイルがそれほど多くないので、add_subdirectories()を使用せず、相対パスでファイルを指定しています。

また、今回はgoogletestを使用するため、リンクするライブラリの名前およびファイルが格納されたディレクトリのパスを、それぞれtarget_link_libraries()とtarget_link_directories()で指定しています。
ディレクトリについては、cmakeはデフォルトでは64bitのビルド環境を生成します。
そのため、x64のgoogletestのライブラリが格納されたフォルダを指定しています。

…と、まぁ、今感じでCMakeLists.txtは、特筆すべきことがないCMakeListst.txtを用意します。

3. cmakeの実行~ビルド

CMakeLists.txtが用意できたので、次はビルドを行ってみます。
ビルドは、以下のコマンドで実行します。

C:\Usertmp\cmake_autotest_sample\sample_function_001_test>mkdir build
C:\Usertmp\cmake_autotest_sample\test\utset\sample_function_001_test>cd build
C:\Usertmp\cmake_autotest_sample\test\utset\sample_function_001_test>cmake ..
C:\Usertmp\cmake_autotest_sample\test\utset\sample_function_001_test>cmake --build .

これらのコマンドを実行した結果は、以下の通りです。

E:\tmp\cmake_autotest_sample\test\utest\sample_function_001_test>mkdir build

E:\tmp\cmake_autotest_sample\test\utest\sample_function_001_test>cd build

E:\tmp\cmake_autotest_sample\test\utest\sample_function_001_test\build>cmake ..
-- Building for: Visual Studio 17 2022
-- Selecting Windows SDK version 10.0.20348.0 to target Windows 10.0.19045.
-- The C compiler identification is MSVC 19.32.31335.0
-- The CXX compiler identification is MSVC 19.32.31335.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.32.31326/bin/HostX64/x64/cl.exe - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.32.31326/bin/HostX64/x64/cl.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: E:/tmp/cmake_autotest_sample/test/utest/sample_function_001_test/build
E:\tmp\cmake_autotest_sample\test\utest\sample_function_001_test\build>cmake --build .
MSBuild version 17.3.1+2badb37d1 for .NET Framework
  Checking Build System
  Building Custom Rule E:/tmp/cmake_autotest_sample/test/utest/sample_function_001_test/CMakeLists.txt
  sample_function_001_test.cpp
  sample_src.cpp
  コードを生成中...
gtestd.lib(gtest-all.obj) : warning LNK4099: PDB 'gtestpdb_debug_postfix-NOTFOUND.pdb' が 'gtestd.lib(gtest-all.obj)' で、または 'E:\tmp\cmake_autotest_sample\test\utest\sample_function_001_test\build\Debug\gtestpdb_debug_postfix-NOTFOUND.pdb' に見つかりません。デバッグ情報がないものとして、オブジェクトにリンクします。 [E:\tmp\cmake_autotest_sample\test\utest\sample_function_001_test\build\sample_function_001_test.vcxproj]
gtest_maind.lib(gtest_main.obj) : warning LNK4099: PDB 'gtest_mainpdb_debug_postfix-NOTFOUND.pdb' が 'gtest_maind.lib(gtest_main.obj)' で、または 'E:\tmp\cmake_autotest_sample\test\utest\sample_function_001_test\build\Debug\gtest_mainpdb_debug_postfix-NOTFOUND.pdb' に見つかりません。デバッグ情報がないものとして、オブジェク トにリンクします。 [E:\tmp\cmake_autotest_sample\test\utest\sample_function_001_test\build\sample_function_001_test.vcxproj]
  sample_function_001_test.vcxproj -> E:\tmp\cmake_autotest_sample\test\utest\sample_function_001_test\build\Debug\sample_function_001_test.exe
  Building Custom Rule E:/tmp/cmake_autotest_sample/test/utest/sample_function_001_test/CMakeLists.txt

前半3つのコマンドは、一括で実行することも可能です。
その場合には、以下のコマンドを実行します。

C:\Usertmp\cmake_autotest_sample\test\utset\sample_function_001_test>cmake -S . -B build
C:\Usertmp\cmake_autotest_sample\test\utset\sample_function_001_test>cmake --build build

実行結果は、前出のログとほとんど変わらないので、省略します。

この一連のコマンドを実行することで、テストのソリューションファイル、および実行ファイルが生成されます。

4. 実行してみる

前章でビルドしたテストを実行してみます。

VisualStudioとgoogletestの組み合わせでは、テストは実行ファイルとして出力されます。
今回のエントリでは、実行ファイルは以下の場所に出力されます。

E:\tmp\cmake_autotest_sample\test\utest\sample_function_001_test\build\Debug\sample_function_001_test.exe

このファイルを実行すると、以下のようになります。



テストが実行できていることが分かります。
(本エントリではテストの「内容」には触れていないので、「生成された実行ファイルでテストが実行されている」ことだけ確認しています。)

5. もう少し自動化してみる

cmakeでのテストのビルド、および実行ができることが分かりました。
しかし、それでもcmakeの各コマンドの実行、及びテストの実行を手動で実施する必要があります。
そのため、これらの手順を自動化してみます。

5.1. 自動化の方法

自動化ですが、「batファイル」で実現します。

5.2. 自動化の実装

batファイルに実装する内容は、以下の通りです。

  1. cmakeによるビルドの実行
  2. テストの実行

やることは2つだけなので、実にシンプルです。
そして、これらの処理を行う処理をbatファイルは、以下のように実装できます。

@echo off
	
cd %~dp0

SETLOCAL

rem 各種情報の設定
SET SRC_DIR=.
SET BUILD_DIR=build
SET TEST_NAME=sample_function_001_test

rem 1. cmakeによるビルドの実行
cmake -S %SRC_DIR% -B %BUILD_DIR%
cmake --build %BUILD_DIR%

rem 2. テストの実行
cd build\Debug\
%TEST_NAME%.exe

ENDLOCAL

5.3. 自動化した結果の実行

自動化に必要なbatファイルの実装が分かった/できたトコロで、それを実行してみます。
実行した結果は、以下の通りです。



このように、cmakeによるビルド~テストの実行までが一括で実行できていることが分かります。

6. まとめ

今回は、テストコードの生成から実行までの自動化を行ってみました。
cmakeとbatファイルを用いることで、コードのビルドから実行までを一括で実行できるようにできました。

しかし、それでもまた「batファイルの作成」と「batファイルの実行」は手動になってしまいます。
加えて本エントリで書いた内容は、全てデスクトップPCでの実行になります。
そのため、仮に複数人でテストを行うとなった場合には、環境が統一されない、管理の仕方次第では諸々競合が発生する可能性があります。
まだまだ解決すべき問題は沢山ありそうです…。

こういった問題については、1つ1つ順番に解決していきます。
(頑張ります!)

ではっ!