「C言語向けBDDフレームワーク CSpecについて - 千里霧中の犀」に続く記事です。
近年のアジャイルコミュニティでのテストの発展を考慮すると、C言語のユニットテストは枯れたクラスタと言えます。が、とはいっても一方でユニットテスティングフレームワークは山のように存在します。逐一紹介していくときりがないので、とりあえずフレームワークの紹介は今回を最後にしたいと思います。
今回はCUTとCuTestという2つのフレームワークを紹介します。機能は似たり寄ったりですが、テストコードの自動生成方法に違いがあります。
紹介していないテスティングフレームワークについて
なおTDD界隈で有力でありながら、一連の複数回のエントリで紹介できなかったフレームワークにCutterとCUnitがあります。CUTとCuTestの具体的な説明に入る前に、一応それらについて簡単に触れておきます。
Cutter
まずCutter(C言語・C++言語用テスティングフレームワーク - Cutter)は、C/C++向けのxUnitベースのユニットテスティングフレームワークです。CppUTest等と違って元々C言語専用のフレームワークとして開発されてきた経緯から、純粋なC言語環境でもライブラリのビルドといった環境構築が可能です。
Cutterは日本人が開発したフレームワークであるため、メリットとして日本語の資料やサポートが充実しています。特に開発者が経営する会社によって有償サポートが提供されているのが異色です。
なお自動テストディスカバリ機能、パラメタライズドテスト、テストの並列実行と、現代的なユニットテストの手法や運用環境を支える機能を持っています。導入に手間がかかること、開発環境に縛りがあることがデメリットですが、それが問題にならなければ、十分推奨できるフレームワークだと思います。機会があれば別途紹介したいと思います。
CUT(C Unit Tester)について
特徴
- C言語向けユニットテスティングフレームワーク。
- サイト: http://www.falvotech.com/content/cut/2.6/
- TDDに特化したシンプルな機能。4フェーズテストをサポートしたAssertion集のような構成。
- Pythonか実行ファイルによってTest Runnerを自動生成できる。
例
CUTはシンプルな構成、自動テストディスカバリ機能、軽快な実行処理と、機能や構成がTDDに特化しているのが特徴です。
テストメソッドは以下の通りです。名前が「__CUT__Test」から始まります。
//testHoge.c #include <cut/2.6/cut.h> #include "hoge.h"//テスト対象 void __CUT__TestHoge(void) { ASSERT(2 == hoge(), "Hoge"); }
なおCUTは、テストメソッドを自動で検索・実行してくれる、テストランナーの自動生成機能を提供しています。その機能はCUTをインストールした際に得られる「cutgen」「cutgen.py」を使用して実現します。
まずインストールはCUTのディレクトリで「make install」あるいは「make install PREFIX=(環境に合わせた適切なパス)」を実行して行います。
そして例えば「cutgen testHoge.c >main.c」と実行すると、以下のようなソースコードからなるmain.cが生成されます。これをビルドして実行すれば、先ほどのテストメソッドが実行されテスト結果が表示されます。
#include <stdlib.h> #include <cut/2.6/cut.h> extern __CUT__TestHoge(void); int main(int argc, char *argv[]) { __cut_assumeDefaults(); __cut_initializeFromArguments_(argc, argv); __CUT__TestHoge(); return 0; }
CuTestについて
特徴
- C言語向けユニットテスティングフレームワーク。
- サイト: http://sourceforge.net/projects/cutest/
- CUTと似ています。シンプルな構成、軽快な実行処理、自動テストディスカバリ機能と、TDDなどテストファーストプログラミングを意識した特徴を持ちます。CUTと比べユニットテスティングフレームワークとしての体裁を整えています。
- 自動テストディスカバリ機能については、Pythonスクリプトや実行ファイルで実現するCUTと違って、シェルスクリプトで実現しています。
例
CuTestのテストメソッドは以下のようになります。
//hogetest.c #include <CuTest.h> void TestHoge(CuTest* tc) { CuAssertIntEquals(tc, 4, hoge()); }
次に自動テストディスカバリ機能についてですが、CuTestに同梱されている「make-tests.sh」を使うと、上記テストメソッドを実行してくれるテストランナーを自動生成できます。
例えば「sh make-tests.sh hogetest.c >main.c」を実行すると、以下のような内容のファイルが生成されます。
/* This is auto-generated code. Edit at your own peril. */ #include <stdio.h> #include <stdlib.h> #include "CuTest.h" extern void TestHoge(CuTest*); void RunAllTests(void) { CuString *output = CuStringNew(); CuSuite* suite = CuSuiteNew(); SUITE_ADD_TEST(suite, TestHoge); CuSuiteRun(suite); CuSuiteSummary(suite, output); CuSuiteDetails(suite, output); printf("%s\n", output->buffer); CuStringDelete(output); CuSuiteDelete(suite); } int main(void) { RunAllTests(); }
これを実行すれば、先ほどのテストメソッドが実行され、結果が表示されます。
なお「make-tests.sh」の仕組みについてですが、その一部を以下に抜粋します。
かなりの力技ですが、grepで「void Test」から始まる行を収集し、それをRunAllTestsにリストアップするやり方を取っています。
echo ' /* This is auto-generated code. Edit at your own peril. */ #include <stdio.h> #include <stdlib.h> #include "CuTest.h" ' cat $FILES | grep '^void Test' | sed -e 's/(.*$//' \ -e 's/$/(CuTest*);/' \ -e 's/^/extern /' echo \ ' void RunAllTests(void) { CuString *output = CuStringNew(); CuSuite* suite = CuSuiteNew(); ' cat $FILES | grep '^void Test' | sed -e 's/^void //' \ -e 's/(.*$//' \ -e 's/^/ SUITE_ADD_TEST(suite, /' \ -e 's/$/);/' echo \ ' CuSuiteRun(suite); CuSuiteSummary(suite, output); CuSuiteDetails(suite, output); printf("%s\n", output->buffer); CuStringDelete(output); CuSuiteDelete(suite); } int main(void) { RunAllTests(); } '