とにかく書く

日々の雑感や知り得たことを、とにかく書いています

Google Test でC言語のプログラムをテストする

Google TestC言語のプログラムをテストするようにできたのでメモ書き。

C言語でプログラムを書いていて、CUnit でテストコードを書いていたのだけれど、CU_ASSERT_EQUAL などで failure になっても「どんな値で failure になったのか」が分からなかった。Google Test なら

  • failure 時に、どういう結果のためにfailure かがわかる
  • テストの追加が簡単にできる
  • テスト結果が見やすい

ためにやってみた。
以下の手順で行った。各ファイルは最後に書く。

  1. テスト用 main 関数を作る(test_main.cpp)
  2. hoge.c にある関数 increment のテストプログラムを作る(test_hoge.cpp)
    • hoge.c の関数プロトタイプ宣言は hoge.h に記述している
  3. hoge.c と test_hoge.cpp, test_main.cpp をコンパイル、リンクする(Makefile)
    • このときテスト対象のプログラムは main.c に main 関数が書いてあるため除外した

ひとつハマったのは、C言語

typedef enum {
	RED, GREEN, BLUE
} COLOR;

などとして、COLOR型を引数に取る関数 bar のテストを

	EXPECT_EQ(ERROR_ARGUMENT, bar(3));

と書いていたらエラーが出た。

	EXPECT_EQ(ERROR_ARGUMENT, bar((COLOR)3));

とキャストすればOK。問題なくテストできる。
テストは C++ で書くので、COLOR 型の関数はあるけど int 型の関数はないよ!って判断された。

test_main.cpp

#include "gtest/gtest.h"

int main(int argc, char *argv[])
{
	::testing::InitGoogleTest(&argc, argv);
	return RUN_ALL_TESTS();
}


test_hoge.cpp

#include "gtest/gtest.h"
extern "C"
{
#include "hoge.h"
}

TEST(foo, testOfFoo)
{
	EXPECT_EQ(1, increment(0));
	EXPECT_EQ(2, increment(1));
}

ちなみに、hoge.h は

#ifndef HOGE_H
#define HOGE_H

int increment(int i);

#endif

hoge.c は、

#include "hoge.h"

int increment(int i)
{
	return i + 1;
}

とした。
最後に Makefile

TARGET=a.out
CC = clang
CPP = clang++
# ===== テスト対象のプログラム作成用 =====
CFILES=$(wildcard *.c)
SRCS=$(CFILES)
HEADERS=$(wildcard *.h)
OBJS=$(SRCS:.c=.o)
INCLUDE=$(addprefix -I./,$(filter-out $(NOMAKEDIR), $(shell find * -type d)))
CFLAG = -Wall
# ===== テスト用 =====
# ./test をテスト用実行ファイルとする
TEST_TARGET=test
# テスト対象のソースファイル(.c ファイル)
TEST_SRCS_C=$(filter-out main.c, $(CFILES)) # main.c 以外の .c ファイル
# テスト用のソースファイル(.cpp ファイル)
TEST_SRCS_CPP=$(wildcard *.cpp)
TEST_OBJS=$(TEST_SRCS_C:.c=.o) $(TEST_SRCS_CPP:.cpp=.o)
TEST_LINK= -lgtest

#--------------------
all: $(TARGET)

$(TARGET): $(OBJS)
	$(CC) -o $@ $(LINK) $(OBJS)

.c.o:
	$(CC) $(CFLAG) $(INCLUDE) -c -o $@ $<

.cpp.o:
	$(CPP) $(CFLAG) $(INCLUDE) -c -o $@ $<

test: $(TEST_OBJS)
	$(CPP) -o $@ $(TEST_OBJS) $(TEST_LINK)

clean:
	rm -f $(TARGET) $(TEST_TARGET) *.o