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

C++やC言語作ったライブラリをテストしたいときに、GoogleTestを使ってテストを書くことができます。
名前の通り、googleが作ったテストフレームワークです。

- [google/googletest: Google Test](https://github.com/google/googletest)

## サンプルコード

まずは、どのような感じでテストが書けるのか紹介します。
たとえば次のようなコードがあるとします。

include/myint.h

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

src/myint.cpp

```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

```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を使います。

```bash
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

```mf
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/google/googletest/blob/master/googletest/docs/Primer.md)

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

今回のコードは、

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

にあります。