S-JIS[2013-01-08]

PCUnit

PCUnitは、C言語用の単体テスト自動実行ツール。(JUnitのC言語版)


概要

PCUnit(Portable C Unit Testing Framework)は、JUnitのC言語版のようなフレームワーク 。

テスト結果が正しいかどうかを比較する関数や、テストを実行する関数を提供している。
Cutterの方が高機能だが、PCUnitは名前の通りポータブル。MicrosoftのしょぼいCコンパイラー(cl.exe)でも使うことが出来る。


インストール

  1. PCUnitのGitHubで「ZIP」ボタンを押してアーカイブファイル(PCUnit-master.zip)をダウンロードする。
  2. 適当な場所に解凍する。(PCUnit-masterというディレクトリーが出来る)
  3. コンパイルしてライブラリーを作成する。

PCUnitを試しに使ってみる為の最小限のサンプル。

examle.c(テスト対象のソース):

int add(int n, int m)
{
	return n + m;
}

テスト対象のソースは、何の変哲も無い普通のC言語のソース。

test_example.c(テストを実行するPCUnitのソース):

#include <PCUnit.h>

extern int add(int n, int m);

void test_add(void)
{
	PCU_ASSERT_EQUAL(3, add(1, 2)); //add(1, 2)の結果が3であることを確認する
}

//「add」のテストsuiteを返す
PCU_Suite *add_suite(void)
{
	static PCU_Test tests[] = {
		PCU_TEST(test_add),
	};
	static PCU_Suite suite = { "add-test", tests, sizeof tests / sizeof tests[0] };
	return &suite;
}

テスト実行用のソースでは、PCUnit.hをインクルードする。
実行結果の判定には「PCU_ASSERT」で始まる関数(マクロ)が用意されているので、それを使用する。

テスト実行用の関数はPCU_Testの配列としてまとめ、PCU_Suiteを返すような関数を準備する。

test.c(テスト実行用のmain):

#include <PCUnit.h>
#include <stdio.h> // for putchar

PCU_Suite *add_suite(void);

int main()
{
	const PCU_SuiteMethod suites[] = {
		add_suite,
	};
	PCU_set_putchar(putchar);
	PCU_run(suites, sizeof suites / sizeof suites[0]);

	return 0;
}

main()関数で各テストソースのsuiteを記述し、実行させる。
2013/1/8時点のPCUnitの場合、テストが成功しても失敗しても0を返す(PCU_run()はvoidなので判定できない)。→改造

コンパイル

テスト対象のソースは普通にコンパイルする。
テスト実行用ソースもコンパイルは普通にするが、PCUnit.hの場所を指定する必要がある。
テスト実行モジュールのリンク時にはPCUnitのライブラリーも必要(gccの場合はlibpcunit.a、cl.exeの場合はPCUnit.obj等全て)。

$ gcc -c example.c -o example.o
$ gcc -c test_example.c -o test_example.o -IPCUnit-master/PCUnit
$ gcc -c test.c         -o test.o         -IPCUnit-master/PCUnit
$ gcc *.o -o test.out PCUnit-master/PCUnit/libpcunit.a

テストの実行

$ ./test.out

Suite: add-test

1 Tests, 0 Failures, 0 Skipped
OK

テストの実行は、コンパイルして出来たプログラムを実行する。


テストが失敗した場合は失敗した場所(テスト関数名・ファイル名・行番号)や値も表示される。

// 失敗するテストを追加
void test_add_fail(void)
{
	PCU_ASSERT_EQUAL(4, add(1, 2)); //わざと失敗するテスト
}
〜
	static PCU_Test tests[] = {
		PCU_TEST(test_add),
		PCU_TEST(test_add_fail),
	};
$ ./test.out

Suite: add-test

Test: test_add_fail
 test_example.c:12: PCU_ASSERT_EQUAL(4, add(1, 2))
  expected : 0x00000004 (4)
  actual   : 0x00000003 (3)

2 Tests, 1 Failures, 0 Skipped

CutterとPCUnitの比較

PCU_ASSERT_EQUAL等の種類はCutterより少ないが、似ている。
CutterからPCUnitに移行する場合は、概ね単純に置換していけばよい。

JUnit3とCutterの例
  Cutter CUnit
ヘッダーファイル cutter.h PCUnit.h
intの判定 cut_assert_equal_int(expected, actual); PCU_ASSERT_EQUAL(expected, actual);
文字列の判定 cut_assert_equal_string(expected, actual); PCU_ASSERT_STRING_EQUAL(expected, actual);
真偽値の判定 cut_assert_true(actual);
cut_assert_false(actual);
PCU_ASSERT_TRUE(actual);
PCU_ASSERT_FALSE(actual);
ポインターの判定 cut_assert_equal_pointer(expected, actual); PCU_ASSERT_PTR_EQUAL(expected, actual);
メモリーの判定 cut_assert_equal_memory(ex, exSize, ac, acSize); PCU_ASSERT_EQUAL(exSize, acSize);
for (i = 0; i < exSize; i++) {
  PCU_ASSERT_EQUAL(ex[i], ac[i]);
}
nullの判定 cut_assert_null(actual);
cut_assert_not_null(actual);
PCU_ASSERT_PTR_NULL(actual);
PCU_ASSERT_PTR_NOT_NULL(actual);
テスト失敗 cut_fail("message"); PCU_FAIL("message");
cut_fail("i=%d", i); 出来ない(PCU_MESSAGEで別に出力する)

テスト失敗時に終了コードを変える

残念ながら、2012/1/8時点のPCUnitでは テストが成功しても失敗しても終了コードが変わらないので、
CI(継続インテグレーション)等のツールで判定することが出来ない。

ただ、PCUnitのソースは分かり易いので、改造してみた。

PCUnit.h:

void PCU_run(const PCU_SuiteMethod *suite_methods, int num);
↓
int PCU_run(const PCU_SuiteMethod *suite_methods, int num);

PCUnit.c:

static int run_all(const PCU_SuiteMethod *suite_methods, int num)
{
	int failed;	// 追加
	int i;
	const PCU_SuiteMethod *method = suite_methods;
	for (i = 0; i < num; i++, method++) {
		PCU_Suite *p = (*method)();
		print_before_test(p);
		PCU_Suite_run(p);
		PCU_Suite_get_result(p, &suite_result);
		add_result();
		PCU_puts("\n");
		print_after_test(p);
	}
	failed =  result.num_suites_failed;	// 追加
	reset(suite_methods, num);
	return failed;	// 追加
}

int PCU_run(const PCU_SuiteMethod *suite_methods, int num)
{
	reset(suite_methods, num);
	return run_all(suite_methods, num);
}

run_all()とPCU_run()の戻り型をintに変える。
そして、resultに保持されている失敗件数を返すようにする。

テスト用main()の方は以下の様に変える。(失敗件数が無ければ0、あれば1を返す)

int main()
{
〜
	return PCU_run(suites, sizeof suites / sizeof suites[0])==0 ? 0 : 1;
}

C言語目次へ戻る / 技術メモへ戻る
メールの送信先:ひしだま