Help us understand the problem. What is going on with this article?

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のリポジトリからソースを引っ張る。
ブラウザでダウンロードしてきて解凍してもかまわない。
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

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

my_func.cpp
#include "my_func.hpp"

int MyFunc() {
    return 10;
}
my_func.hpp
#ifndef MYFUNC_HPP
#define MYFUNC_HPP

int MyFunc();

#endif

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

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

その他のアサーションマクロは公式ドキュメントや日本語訳を参照すべし。

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

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

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

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

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が生成されるので実行すれば

$ ./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のビルドディレクトリになっているみたいだが…
とりあえず導入完了。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした