|
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桁以上大きくなってしまったけれど. [これ]
ただ色々付け加えた後に、ひさしぶりに元の(上記の)ソースみると、 蛇足だったかなあ、という気にもなる. ※そういや、グローバルのコンストラクタって処理順保証なしだから マルチスレッドでばらばらに実行される可能性ってあるのかな? (当然そうだと今のままでは破綻) |