GoogleTest導入

最近趣味でもC++を本格的に始めたのでテスト環境としてGoogle Testを導入したのでメモ書き。

# Google Testとは
Google's C++ test frameworkが正式名称なのかな？
Googleが開発、公開しているC++用単体テストフレームワーク。
ASSERTを組み込んだテストコードを自動で走らせてくれるスグレモノ。

## そもそも単体テストとは？
最小に近い単位の機能の動作を検証するもの。
いわゆるV字モデルの詳細設計に対応するテスト工程になる。
https://ja.wikipedia.org/wiki/V%E3%83%A2%E3%83%87%E3%83%AB

やれドメイン駆動開発だのユースケース駆動開発だの、それらを持ち込んだクリーンアーキテクチャだの言われますが、つまるところはオブジェクト指向の根本となるカプセル化をちゃんとやれということで、カプセル化が正しくなされていれば関数やクラスは機能単位で分解できるはず。そうすると単体テストもしやすくなってコードの質も上がっていく、という考え。

逆から見て行うべきテストを先に書いて、それにあわせて実装していくというテスト駆動開発というのもありますね。
私は苦手ですが。

# 使用方法
Win10Home+cygwin+g++(gcc)での導入。
## ダウンロード & ビルド
Githubの[リポジトリ](https://github.com/google/googletest)からソースを引っ張る。
ブラウザでダウンロードしてきて解凍してもかまわない。
2019/10/15現在は1.10.0が最新。以降Verは適当に置き換えること。

```
wget https://github.com/google/googletest/archive/release-1.10.0.tar.gz
tar -xvf release-1.10.0.tar.gz
```

googletest-release-1.10.0のディレクトリで解凍されるので内部へ移動しビルドを行う。
ビルドされたいファイルが多数生成されてごちゃごちゃしてしまうのでビルド用ディレクトリを作成してその中でcmakeを走らせる。

```
cd googletest-release-1.10.0
mkdir build-tmp
cd build-tmp
cmake ..
make
```

この時点で以下のようなディレクトリになっているはず。
（細かいのは省略）

```
googletest-release-1.10.0/
├── build-tmp
│   ├── bin
│   ├── CMakeFiles
│   ├── googlemock
│   ├── googletest
│   ├── lib
│   │   ├── libgmock.a
│   │   ├── libgmock_main.a
│   │   ├── libgtest.a
│   │   └── libgtest_main.a
│   ├── cmake_install.cmake
│   └── Makefile
├── ci
├── googlemock
├── googletest
│   ├── cmake
│   ├── include
│   │   ├── gtest
│   ├── src
│   └── LICENSE
├── CMakeList.txt
├── LICENSE
```

この中から使用するのは```./build-tmp/lib/```内の2つのgtestライブラリファイルと```./googletest/include/gtest```ディレクトリまるっと。

# テストの走らせ方
ここでは以下のようなプロジェクトディレクトリ構成を扱う。
プロジェクトルート直下に```googletest```をディレクトリを作成し、ライブラリファイル2つと```googletest-release-1.10.0/googletest/include/gtest```を持ってくる。

```
./
├── bin
├── googletest
│   ├── include
│   │   └── gtest
│   │       ├── gtest.h
│   │       └── ...
│   ├── libgtest.a
│   └── libgtest_main.a
├── src
│   └── my_func.cpp
├── test
│   └── test_my_func.cpp
└── makefile
```

とりあえずテスト対象の関数。

``` C++:my_func.cpp
#include "my_func.hpp"

int MyFunc() {
    return 10;
}
```

``` C++:my_func.hpp
#ifndef MYFUNC_HPP
#define MYFUNC_HPP

int MyFunc();

#endif
```

テストコードは```test```ディレクトリに追加していく。

``` C++:test_my_func.cpp
#include "gtest/gtest.h"

#include "my_func.hpp"

TEST(MyTest, MyFunctionTest) {
    int response = MyFunc();
    EXPECT_EQ(response, 10);
}
```

テストコードでは```gtest/gtest.h```をインクルードする。
テストケースはTESTマクロで記述する。TESTマクロの第1引数はテストケース名、第2引数にテスト名を指定する。もちろんテストケース内でテスト名が被ると動作しない（はず）なので注意。

実際のテスト判定にはアサーションマクロを定義する。
アサーションには```ASSERT```系と```EXPECT```系が用意されている。
```ASSERT```系ではひっかかったときに、その時点でテストをそこで終了させる。これに対し```EXPECT```系ではテストは全て実行される。ひっかかるとその後がすべて無為になるような場面では```ASSERT```を使い、基本的には```EXPECT```系を使えばいいと思う。
```ASSERT```/```EXPECT```どちらでもいくつかの種類が用意されている。

|            |     ASSERT    |    EXPECT    |
|:-----------|:-------------:|:------------:|
| TrueでOK   |  ASSERT_TRUE  | EXPECT_TRUE  |
| FalseでOK  |  ASSERT_FALSE | EXPECT_FALSE |
| == でOK    |  ASEERT_EQ    | EXPECT_EQ    |
| >= でOK    |  ASSERT_GE    | EXPECT_GE    |

その他のアサーションマクロは公式ドキュメントや[日本語訳](http://opencv.jp/googletestdocs/primer.html)を参照すべし。

これらをコンパイルする必要があるがいくつかハマった点があったので、それを含め注意点をメモ。

 * C++ Verの指定オプションではc++ではなくgnu++を用いる
googletest内部で用いられる関数などがANSI C++標準ではなくGNU拡張が含まれたものになっている。
したがってコンパイルオプションで```-std=c++11```とするところを```-std=gnu++11```とする必要がある。
もしかするとgoogletestのmake時にオプションを定義すれば不要になるかもしれない。

 * gtestまわりをリンクさせる
.aライブラリファイル2つと同時にいくつかコンパイル時にリンクさせる必要がある。また```-pthread```も必要。

これらを踏まえたmakefileが以下。
汎用的ではなくこのテストに限定したものなので、実際にはdebugビルドやreleaseビルドなどのルールを追加したmakefileにする。

```make
CXX = g++
CXXFLAGS = -std=gnu++11

TARGET = Sample.exe
TARGETDIR = ./bin

INCLUDES = -I. -Isrc -Igoogletest/include

SRCDIR = ./src
SRCS = $(SRCDIR)/my_func.cpp
OBJS := $(SRCS:.cpp=.o)

TESTDIR = ./test
TESTSRCS = $(TESTDIR)/test_my_func.cpp
TESTOBJS := $(TESTSRCS:.cpp=.o)

GTESTLIB += ./googletest/libgtest.a ./googletest/libgtest_main.a
GTESTFLAGS = -Lgoogletest -lgtest -lgtest_main -lpthread

.PHONY: all clean
.SUFFIXES: .c .cpp .o

all: $(TARGETDIR)/$(TARGET)

$(TARGETDIR)/$(TARGET): $(OBJS) $(TESTOBJS)
	$(CXX) $(GTESTFLAGS) -o $@ $^ $(GTESTLIB)

.cpp.o:
	$(CXX) $(INCLUDES) $(CXXFLAGS) -o $@ -c $^
.cpp:
	$(CXX) $(INCLUDES) $(CXXFLAGS) -o $@ $^

clean:
	rm $(OBJS) $(TESTOBJS)
```

ビルドを走らせれば```./bin/Sample.exe```が生成されるので実行すれば

```console
$ ./bin/Sample.exe
Running main() from /home/usr/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 MyTest
[ RUN      ] MyTest.MyFunctionTest
[       OK ] MyTest.MyFunctionTest (0 ms)
[----------] 1 test from MyTest (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 1 test.

```
のような形でリンクされているテストコードで定義されているTESTマクロが全て実行される。

gtest_mainの参照先がgoogletestのビルドディレクトリになっているみたいだが…
とりあえず導入完了。