Edited at

GoogleTestでC++のコードをテストする

More than 3 years have passed since last update.

C++やC言語作ったライブラリをテストしたいときに、GoogleTestを使ってテストを書くことができます。

名前の通り、googleが作ったテストフレームワークです。


サンプルコード

まずは、どのような感じでテストが書けるのか紹介します。

たとえば次のようなコードがあるとします。

include/myint.h

class MyInt

{
public:
MyInt(int num);
bool isOdd();
bool isEven();
private:
int num_;
};

src/myint.cpp

#include <myint.h>


MyInt::MyInt(int num)
:num_(num)
{}

bool MyInt::isOdd()
{
// 奇数かどうかを判定する
return (num_ % 2) != 0 ? true : false;
}

bool MyInt::isEven()
{
return isOdd(); // 例として、ここにバグがある
}

すると、テストは次のように書けます。

test/gtest_myint.cpp

#include "gtest/gtest.h"

#include "myint.h"

namespace {

class MyIntTest : public ::testing::Test{};

TEST_F(MyIntTest, isOdd)
{
MyInt mi1 = MyInt(10);
EXPECT_EQ(false, mi1.isOdd());

MyInt mi2 = MyInt(13);
EXPECT_EQ(true, mi2.isOdd());
}

TEST_F(MyIntTest, isEven)
{
MyInt mi1 = MyInt(10);
EXPECT_EQ(true, mi1.isEven());

MyInt mi2 = MyInt(13);
EXPECT_EQ(false, mi2.isEven());
}

} // namespace

このようなテストを書いた場合、GoogleTestをビルドしてからテストのビルドを行います。


GoogleTestのビルド

GoogleTestのコードは次のようにして取得できます。

git clone git@github.com:google/googletest.git


OSX(Yosemite)でのビルド

CXX=/usr/bin/clang++ CC=/usr/bin/clang ./configure 'CXXFLAGS=-std=c++11 -stdlib=libc++'

CXX=/usr/bin/clang++ CC=/usr/bin/clang make


Ubuntu(14.04)でのビルド

cmakeを使います。

sudo apt-get install cmake

mkdir build
cd build
cmake ..
make


テストのビルド

たとえば次のようなディレクトリ構成の場合、したのようなMakefileが書けると思います。

テストをビルドする際にgtestも合わせてビルドすることになります。

.

├── LICENSE
├── README.md
├── include
│   └── myint.h
├── extsrc
│   └── googletest/googletest
├── make
│   └── Makefile
├── src
│   └── myint.cpp
├── test
   └── gtest_myint.cpp

ディレクトリ名等は適宜読み替えてください。

Makefile

SRC_DIR=../src

INC_DIR=../include
TEST_DIR=../test
LIB_DIR=../lib
BIN_DIR=../bin
OBJ_DIR=./obj
GTEST_DIR=../extsrc/googletest/googletest

INCS += -I$(INC_DIR)

UNAME := $(shell uname -s)
ifeq ($(UNAME),Linux)
CXX=g++
endif
ifeq ($(UNAME),Darwin)
CXX=/usr/bin/clang++
endif

CXXFLAGS = -g -Wall
SRCS = $(SRC_DIR)/myint.cpp
TARGET = $(LIB_DIR)/libmyint.a
OBJS = $(addprefix $(OBJ_DIR)/, $(notdir $(SRCS:.cpp=.o)))

default: $(TARGET)
.PHONY: default

$(TARGET): $(OBJS)
@[ -d $(LIB_DIR) ] || mkdir -p $(LIB_DIR)
$(AR) ruc $(TARGET) $(OBJS)

$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp
@[ -d $(OBJ_DIR) ] || mkdir -p $(OBJ_DIR)
$(CXX) $(CXXFLAGS) $(INCS) -o $@ -c $<

TEST_SRCS = $(TEST_DIR)/gtest_myint.cpp
TEST_TARGET = $(BIN_DIR)/gtest_myint
TEST_OBJS = $(addprefix $(OBJ_DIR)/, $(notdir $(TEST_SRCS:.cpp=.o)))
LIBS += -L$(LIB_DIR)
LIBS += -lmyint

CPPFLAGS += -isystem $(GTEST_DIR)/include
CXXFLAGS = -g -Wall -Wextra -pthread

# All Google Test headers. Usually you shouldn't change this
# definition.
GTEST_HEADERS = $(GTEST_DIR)/include/gtest/*.h \
$(GTEST_DIR)/include/gtest/internal/*.h
GTEST_SRCS_ = $(GTEST_DIR)/src/*.cc $(GTEST_DIR)/src/*.h $(GTEST_HEADERS)

# For simplicity and to avoid depending on Google Test's
# implementation details, the dependencies specified below are
# conservative and not optimized. This is fine as Google Test
# compiles fast and for ordinary users its source rarely changes.

$(OBJ_DIR)/gtest-all.o : $(GTEST_SRCS_)
$(CXX) $(CPPFLAGS) -I$(GTEST_DIR) $(CXXFLAGS) -c \
-o $@ $(GTEST_DIR)/src/gtest-all.cc

$(OBJ_DIR)/gtest_main.o : $(GTEST_SRCS_)
$(CXX) $(CPPFLAGS) -I$(GTEST_DIR) $(CXXFLAGS) -c \
-o $@ $(GTEST_DIR)/src/gtest_main.cc

$(LIB_DIR)/gtest.a : $(OBJ_DIR)/gtest-all.o
$(AR) $(ARFLAGS) $@ $^

$(LIB_DIR)/gtest_main.a : $(OBJ_DIR)/gtest-all.o $(OBJ_DIR)/gtest_main.o
$(AR) $(ARFLAGS) $@ $^

test: $(TEST_TARGET)
.PHONY: test

$(TEST_TARGET): $(TARGET) $(TEST_OBJS) $(LIB_DIR)/gtest_main.a
@[ -d $(BIN_DIR) ] || mkdir -p $(BIN_DIR)
$(CXX) $(LDFLAGS) -o $@ $(TEST_OBJS) \
$(LIB_DIR)/gtest_main.a $(LIBS) -lpthread

$(OBJ_DIR)/%.o: $(TEST_DIR)/%.cpp $(GTEST_HEADERS)
@[ -d $(OBJ_DIR) ] || mkdir -p $(OBJ_DIR)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(INCS) -o $@ -c $<

clean:
rm -f $(TARGET) $(TEST_TARGET) $(OBJS) $(TEST_OBJS)

これで、Makefileがあるディレクトリにおいて

make

make test

とやるとライブラリとテストがビルドできます。


テストの実行と結果確認

上の例では binディレクトリに gtest_myint というバイナリができているので、それを実行すると、

$ ./gtest_myint

Running main() from gtest_main.cc
[==========] Running 2 tests from 1 test case.
[----------] Global test environment set-up.
[----------] 2 tests from MyIntTest
[ RUN ] MyIntTest.isOdd
[ OK ] MyIntTest.isOdd (0 ms)
[ RUN ] MyIntTest.isEven
../test/gtest_myint.cpp:21: Failure
Value of: mi1.isEven()
Actual: false
Expected: true
../test/gtest_myint.cpp:24: Failure
Value of: mi2.isEven()
Actual: true
Expected: false
[ FAILED ] MyIntTest.isEven (0 ms)
[----------] 2 tests from MyIntTest (0 ms total)

[----------] Global test environment tear-down
[==========] 2 tests from 1 test case ran. (0 ms total)
[ PASSED ] 1 test.
[ FAILED ] 1 test, listed below:
[ FAILED ] MyIntTest.isEven

1 FAILED TEST

という感じでテストが失敗することが確認できます。

修正してビルドしなおせばもちろん全てOKになります。


まとめ

GoogleTestを使えばC++のテストが上のような感じでかけます。

慣れるまではMakefileを書く部分でハマったりしますが、一度環境を作ればテストを書くのは比較的簡単だと思います。

テストの書き方などは

googletest/Primer.md at master · google/googletest

が参考になると思います。

今回のコードは、

https://github.com/tjun/googletest-circleci

にあります。