PCUnitは、C言語用の単体テスト自動実行ツール。(JUnitのC言語版)
|
PCUnit(Portable C Unit Testing Framework)は、JUnitのC言語版のようなフレームワーク 。
テスト結果が正しいかどうかを比較する関数や、テストを実行する関数を提供している。
Cutterの方が高機能だが、PCUnitは名前の通りポータブル。MicrosoftのしょぼいCコンパイラー(cl.exe
)でも使うことが出来る。
$ cd PCUnit-master/PCUnit $ make $ ls *.a libpcunit.a
cl.exe
)ではmakeなんて高尚なものは当然使えないので、直接コンパイルする。> cd /d PCUnit-master/PCUnit > cl /c *.c > dir /b *.obj PCUnit.obj PCU_Libc.obj PCU_Suite.obj PCU_Test.obj
PCUnitを試しに使ってみる為の最小限のサンプル。
int add(int n, int m) { return n + m; }
テスト対象のソースは、何の変哲も無い普通のC言語のソース。
#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を返すような関数を準備する。
#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
PCU_ASSERT_EQUAL等の種類はCutterより少ないが、似ている。
CutterからPCUnitに移行する場合は、概ね単純に置換していけばよい。
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); |
PCU_ASSERT_TRUE(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); |
nullの判定 | cut_assert_null(actual); |
PCU_ASSERT_PTR_NULL(actual); |
テスト失敗 | cut_fail("message"); |
PCU_FAIL("message"); |
cut_fail("i=%d", i); |
出来ない(PCU_MESSAGE で別に出力する) |
残念ながら、2012/1/8時点のPCUnitでは テストが成功しても失敗しても終了コードが変わらないので、
CI(継続インテグレーション)等のツールで判定することが出来ない。
ただ、PCUnitのソースは分かり易いので、改造してみた。
void PCU_run(const PCU_SuiteMethod *suite_methods, int num); ↓ int PCU_run(const PCU_SuiteMethod *suite_methods, int num);
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; }