5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

単体テスト環境を作ってみる

Last updated at Posted at 2017-05-05

C言語での単体テスト環境になかなかいいのがないので自作してみる。

目的

  • 通常のビルド環境とは別に単体テストを簡単に実行できる環境を作る。("make test"でテスト実行)
  • テストコードは関数を追加するだけにしたい。

実装結果

テスト対象

main.c
#include <stdio.h>

static int func1(int parm)
{
    if (parm > 0)
        return 1;
    return 0;
}

int main(int argc, char *argv[])
{
    printf("Hello World!\n");
    printf("arg test %d\n", func1(argc));
    return 0;
}

テストコード

ut_main.c
#include <stdio.h>
#include <dlfcn.h>
#include <stdbool.h>

/* test function prototype */
typedef bool (*Test)(void);

#define main __original_main

#include "main.c"

#undef main

#define UT_ASSERT(f) {if (!(f)) {printf("%s:%u: '%s' is NG\n", __FILE__,__LINE__,#f);return false;}}

bool test001(void)
{
    UT_ASSERT(func1(1) == 1);
    return true;
}

bool test002(void)
{
    UT_ASSERT(func1(0) == 1);
    return true;
}

bool test003(void)
{
    UT_ASSERT(func1(-1) == 0);
    return true;
}

int main(int argc, char *argv[])
{
    Test t;
    bool ret;
    int i;
    char func_name[100];
    unsigned int count_ok = 0, count = 0;
    
    for (i = 0; i < 100; i++) {
        sprintf(func_name, "test%03d", i);
        t = (Test) dlsym(RTLD_DEFAULT, func_name);
        if (t != NULL)  {
            count++;
            ret = (*t)();
            if (ret)
                count_ok++;
            else
                printf("test NG %s\n", func_name);
        }
    }
    printf("result %u/%u \n", count_ok, count);
}

Makefile


all: main

#TARGETS
PROGRAM = main
OBJS = main.o
TEST = ut_main
TEST_OBJS = ut_main.o

# ENVIRONMENT
CC ?= gcc
CFLAGS ?= -Wall -O2

# suffixes
.SUFFIXES: .c .o

# target
$(PROGRAM): $(OBJS)
	$(CC) -o $(PROGRAM) $^

# サフィックスルール
.c.o:
	$(CC) $(CFLAGS) -c $<

# ファイル削除用ターゲット
.PHONY: clean
clean:
	$(RM) $(PROGRAM) $(OBJS) $(TEST) $(TEST_OBJS)

.PHONY: test
test: $(TEST)
	./$(TEST)

$(TEST): $(TEST_OBJS)
	$(CC) -o $(TEST) $^

実行例

MAC OS X(10.12.4)

% make test
cc -Wall -O2 -c ut_main.c
cc -o ut_main ut_main.o
./ut_main
ut_main.c:24: 'func1(0) == 1' is NG
test NG test002
result 2/3 

技術的な話

  • dlsymを使ってテスト用関数の名前(文字列)から関数ポインタを取得する。test0xxという名前で適当に追加すれば実行される。dlsymはposix準拠(*1)のはずなので移植性も問題ない(と思いたい)
  • テスト対象を#include することで、staticメンバにもアクセスできるようにする。
  • Makefile内でtestというターゲット名を作り、コンパイルしたテストコードをそのまま実行(emacs内での実行を想定、NG箇所に飛べるように出力形式を合わせる)

参考
1: https://linuxjm.osdn.jp/html/LDP_man-pages/man3/dlsym.3.html

5
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?