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

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

にあります。

tjun
Network software engineer
http://tjun.org
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