目的
業務で10年以上前から利用されてきているレガシーなCコードに対して修正を加えた.修正は,グローバルだけでなくstaticな変数および関数がない関数の追加だけである. その関数が,想定した動きになるかどうか確かめておきたかったため,その関数に対してのみGoogleTestを実行できたのでその結果を記載する.
通常は(?),グローバルだけでなくstaticな変数および関数が存在することが多いので,実施は複雑になると思われる.
ユニットテスト(関数単体テスト)の実施が初めてなので,慣習と大きくズレていたり,より効率のよい方法をご存じの場合はコメント下さい.
環境は以下のとおり*1:
名称 | 規格 | 製造会社 | 備考 |
---|---|---|---|
ホストOS | Windows 10 Pro,バージョン20H2,OSビルド19042.1348 | Microsoft | |
仮想マシン | VirtualBox 6.1.30 r148432 | Oracle | Guest Additions設定済 |
ゲストOS | Ubuntu 16.04.7 LTS | Canonical Ltd. | cat /etc/os-release で確認 |
Linuxカーネル | 4.15.0-142-generic | Linus Torvalds and Community contributors | uname -r で確認 |
CMake | cmake version 3.5.1 | kitware | cmake --version で確認 |
C++コンパイラー | g++ (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0 | Free Software Foundation, Inc. | g++ --version で確認 |
GoogleTest | 1.10.0 | 1.11.0が2021/12/13時点では最新 |
結果
GoogleTestのインストール
1.11.0が今日2021/12/13時点での最新だがmake install
で失敗するため,1.10.0を使用する.
バージョンの確認はこちら☞https://github.com/google/googletest/releases
$ wget https://github.com/google/googletest/archive/refs/tags/release-1.10.0.zip $ unzip release-1.10.0.zip $ cd googletest-release-1.10.0/ $ mkdir build $ cd build $ cmake .. $ sudo make install
ファイル構成とGoogleTest実行まで
以下,実際のコードを書くわけには行かないので,やんわり"ぼかして"記載する.
関数を追加したファイルと追加した関数
ファイル構成を描くと以下で,common
とproc/common
の配下が共通で使用するもの,appl/src
配下を主として使用してひとつのバイナリーを作成している.
また,ApInc.h
にはappl/src
配下のヘッダーファイルがすべてインクルードされている:
├── common │ ├── Cmn.h │ ├── Pk.c │ └── Pk.h └── proc ├── common │ ├── PCmn.h │ └── PCmd.h └── appl └── src ├── Makefile ├── ApInc.h ├── ApMain.c ├── ApMain.h ├── ApFnc.c └── ApFnc.h
ソースコードとしてはApFnc.c
に以下のような関数をファイル末尾に追加した:
uint16_t usGetTimerVal(uint8_t ucTime) { uint16_t usTimer = SEC_10; /* SEC_10はdefine定義値10 */ switch(ucTime) { case SET_TIME_10M: /* SET_TIME_10Mはdefine定義値1 */ usTimer = SEC_600; /* SEC_600はdefine定義値600 */ break; case SET_TIME_10S: /* SET_TIME_10Sはdefine定義値0 */ default: usTimer = SEC_10; /* SEC_10はdefine定義値10 */ break; } return usTimer ; }
一方,ApFnc.h
には上記の宣言extern uint16_t usGetTimerVal(uint8_t);
を追記したのみである.
GoogleTest実行のためのテストファイル
世の中,同一の階層にtest
ディレクトリーを作成してテストコードを書くのが主流なようなので(?),以下のようにtest
ディレクトリーとApFnc.c
に対応するApFncTest.cc
を作成した*2:
├── common │ ├── Cmn.h │ ├── Pk.c │ └── Pk.h └── proc ├── common │ ├── PCmn.h │ └── PCmd.h └── appl ├── src │ ├── Makefile │ ├── ApInc.h │ ├── ApMain.c │ ├── ApMain.h │ ├── ApFnc.c │ └── ApFnc.h └── test ├── Makefile └── ApFncTest.cc
ApFncTest.cc
の中身は以下で,大まかには,使用する関数の読み込みとテストケースを記載するだけである:
#include <gtest/gtest.h> /* ヘッダーファイルのインクルードはMakefileで対応する */ /* 以下のような書き方をするとstaticな変数や関数も使用できるとのこと(参考文献参照) */ namespace unit_test { #include "ApFnc.c" /* usGetTimerVal関数の実態の読み込み */ TEST(usGetTimerValTest, usGetTimerVal) { /* 正常系 */ /* case SET_TIME_10Mのパスを通る */ EXPECT_EQ(SEC_600, usGetTimerVal(SET_TIME_10M)); /* case SET_TIME_10Sのパスを通る */ EXPECT_EQ(SEC_10, usGetTimerVal(SET_TIME_10S)); /* 異常系 */ /* defaultのパスを通る */ EXPECT_EQ(SEC_10, usGetTimerVal(2)); /* defaultのパスを通る */ EXPECT_EQ(SEC_10, usGetTimerVal(-1)); } }
また,上記をビルドするMakefile
は以下である:
TARGET_TEST=appl_test BUILD_ROOT_PATH=../../.. # GoogleTestにはC++コンパイラーを使用 CC=g++ # GoogleTestでのコンパイル時にエラーが出るのでC++11またはgnu++11を使用(後述) CXXFLAGS=-std=c++11 # テストコードが追加の場合はここにソースを追加 SRCS=ApFncTest.cc # GoogleTestを実行するコード範囲を狭める(後述) DEFS=-DEXEC_GTEST # GoogleTest独自のものだけでなくpthreadライブラリーも必要 LIBS=-lgtest_main -lgtest -lpthread # 共通のヘッダーとapplビルド時に参照するヘッダーを指定 INCDIR= -I$(BUILD_ROOT_PATH)/common -I$(BUILD_ROOT_PATH)/proc/common -I../src all: clean $(TARGET_TEST) $(TARGET_TEST): $(CC) $(SRCS) -o $@ $(DEFS) $(CXXFLAGS) $(INCDIR) $(LIBS) clean: -rm -f $(TARGET_TEST)
上記が準備できたら,make all
を実行してTARGET_TEST
であるappl_test
が同一階層に生成される.それを実行することによりテスト結果が得られる:
$ ./appl_test Running main() from /home/so2akt/gtest/googletest-release-1.10.0/googletest/src/gtest_main.cc [==========] Running 1 test from 1 test suite. [----------] Global test environment set-up. [----------] 1 test from usGetTimerValTest [ RUN ] usGetTimerValTest.usGetTimerVal [ OK ] usGetTimerValTest.usGetTimerVal (0 ms) [----------] 1 tests from usGetTimerValTest (0 ms total) [----------] Global test environment tear-down [==========] 1 test from 1 test suite ran. (0 ms total) [ PASSED ] 1 test.
その他
GoogleTestでのコンパイル時にエラー
g++でのコンパイル時に-std=c++11
または-std=gnu++11
のオプションが存在しないと以下のエラーが出た*3:
In file included from /usr/include/c++/5/type_traits:35:0, from /usr/local/include/gtest/gtest.h:59, from ApFncTest.cc:1: /usr/include/c++/5/bits/c++0x_warning.h:32:2: error: #error This file requires compiler and library support for the ISO C++ 2011 standard. This support must be enabled with the -std=c++11 or -std=gnu++11 compiler options. #error This file requires compiler and library support \ ^
実行するコード範囲を狭める
ApFnc.c
に追加したコードはusGetTimerVal
関数のみだが,もちろんそれ以外の多量な関数も同ファイルに存在している.その影響で,それらの関数をビルド対象にするとエラーが大量に出るため,EXEC_GTEST
を定義してusGetTimerVal
関数のみを実行するようにした.この関数はたまたまグローバルだけでなくstaticな関数や変数を参照しないため簡単にGoogleTestを実施することができた.
実際にはもちろん複雑だが以下のとおりである:
#include "ApInc.h" ・・・ static uint8_t G_ucVar = 0; ・・・ enum TAG_INFO { INFO, WARN, ERROR, INFO_MAX }; ・・・ #ifndef EXEC_GTEST void vInit (void) { uint8_t ucVal = 0; G_ucVar = 1; ・・・ } ・・・ #endif /* EXEC_GTEST */ uint16_t usGetTimerVal(uint8_t ucTime) { uint16_t usTimer = SEC_10; /* SEC_10はdefine定義値10 */ switch(ucTime) { case SET_TIME_10M: /* SET_TIME_10Mはdefine定義値1 */ usTimer = SEC_600; /* SEC_600はdefine定義値600 */ break; case SET_TIME_10S: /* SET_TIME_10Sはdefine定義値0 */ default: usTimer = SEC_10; /* SEC_10はdefine定義値10 */ break; } return usTimer ; }
参考URLなど
- [ubuntu]Google Testの導入方法(2021/12/13現在)
- 花井志生:モダンC言語プログラミング,KADOKAWA,2019,pp.151-160(2021/12/13現在)