2010-2-13[土] unittestの作成メモD言語の unittest に触発されて、 C++用に真似たものを作ってみたことがある. 使い方はこんな感じ
// テスト例 #include "unittest.h" class Foo { 略 }; UNITTEST(Foo_Test) { Foo foo; assert(foo.hoge() == 1); 略 }
// 起動ルーチン #include "unittest.h" int main() { UNITTEST_RUN_ALL(); 略 }
といった感じでヘッダファイル一つですむ. そのヘッダの中身は
#ifndef UNITTEST_H #define UNITTEST_H #include <cassert> #ifndef UNITTEST_MAX #define UNITTEST_MAX 256 ///< 登録できるテストの数 #endif #ifndef USE_UNITTEST // テストしないとき. #define UNITTEST_RUN_ALL() #define UNITTEST(T) template<typename D> void uniTTesT_dmy##T() #else // テストするとき. #define UNITTEST_RUN_ALL() uNITtEST_Mgr<>::runAll() #define UNITTEST(T) \ class T { \ public: T() {uNITtEST_Mgr<>::add(&test);} \ private: static void test(); \ }; \ static T uNITtEST_vAr_##T; \ void T::test() template<int N=UNITTEST_MAX> class uNITtEST_Mgr { static unsigned tblNum_; static void* tbl_[N]; //関数ポインタの配列だとvcが落ちたorz. public: static void add(void (*fnc)()) { if (tblNum_ < N) tbl_[tblNum_++] = (void*)fnc; else assert(0 && "too many UNITTEST."); } static void runAll() { for (unsigned i = 0; i < tblNum_; i++) ((void (*)())tbl_[i])(); } }; template<int N> unsigned uNITtEST_Mgr<N>::tblNum_=0; template<int N> void* uNITtEST_Mgr<N>::tbl_[N]; #endif // USE_UNITTEST #endif // UNITTEST_H
テストしないときは、
テストをするときは、 UNITST_TEST()マクロは、ファイルstaticのclass変数を定義にすることで main()実行前にコンストラクタが実行され、 そのコンストラクタ内でテスト関数へのポインタを UnitstMgr::add()で登録している. グローバル変数のコンストラクタの実行順番は不定なので、Mgrの管理する変数はそれらよりも先に初期化されている必要がありstatic変数にしている(ので登録可能なテスト数/配列サイズも固定) そして main()関数の開始時点で、uNITtEST_Mgrにテストがすべて 登録済みなので、UNITST_RUN_ALL ( UnitstMgr::runAll ) を実行すれば、テストが全て実行される.
わりと単純にすむ、というか、すませてる.
まあ、実際に使おうとすると、assertだけでなく、各種チェック用のマクロがそこそこほしくなるし、多少なりとはいえ、経過メッセージをだしたりエラー数カウントしたりしだすと、すぐ1桁以上大きくなってしまったけれど. [これ]
ただ色々付け加えた後に、ひさしぶりに元の(上記の)ソースみると、 蛇足だったかなあ、という気にもなる. ※そういや、グローバルのコンストラクタって処理順保証なしだから マルチスレッドでばらばらに実行される可能性ってあるのかな? (当然そうだと今のままでは破綻) |