eXtreme Programmingテスト技法―xUnitではじめる実践XPプログラミング (OOP foundations)
- 作者: 日本XPユーザグループ,長瀬嘉秀
- 出版社/メーカー: 翔泳社
- 発売日: 2001/11
- メディア: 単行本
- クリック: 20回
- この商品を含むブログ (14件) を見る
第5回は本に載ってない気がする(多分オリジナル)。
前回まででテストスイートとテストランナーを自動生成し、テストコードともどもビルド、実行することができた。
なんだけど、一つだけ不満がある。
それは「テスト対象」が増えるごとに makefile を書き換えないといけないこと。
例で言うと赤の "OBJS" の部分。
OBJS = Quiz.obj
LIBS = cppunit.lib
INC = D:\_work\etc\cppunit-x-20020331test : $(OBJS)
ruby TestRunnerFactory.rb
cl -GX -I$(INC) Tester.cpp $(LIBS) $(OBJS)
Tester.exe
ここまで自動化しといて makefile だけ手動というのはどうかと思うので、これも自動生成できるんじゃないかと試したら。
できたよ^^;
TestRunnerFactory.rb の要領で作ってみた。
自動生成された makefile
makefile を自動生成する Ruby のコードを書いた結果、こんな makefile が出来上がった
OBJS = Counter.obj Quiz.obj LIBS = cppunit.lib INC = D:\_work\etc\cppunit-x-20020331 test : $(OBJS) ruby TestRunnerFactory.rb cl -GX -I$(INC) Tester.cpp $(LIBS) $(OBJS) Tester.exe
makefile を自動生成する Ruby コード
TestRunnerFactory.rb を見よう見まねで作ってみた。
その中で参考にしたサイトは以下の通り
- Ruby の コメント(1)@シリコンバレー 24時
- 文字列中で指定したパターンにマッチする部分を置換する@逆引きRuby
- 秀丸エディタのRuby プログラミング用 強調表示定義ファイルを作りました。@工藤探偵事務所
TestMakefileFactory.rb
getCppList で cpp ファイルを検索し、ヒットしたファイル名の ".cpp" 部分を ".obj" に置換
これを makeMakefile で makefile の形にして出力している
class TestMakefileFactory def initialize @files = [] end def getCppList dir = Dir.open( Dir.pwd ) reg = /(.*\.cpp)/ dir.each { |file| if reg =~ file then # .cpp を .obj に変換して格納 @files.push file.sub( ".cpp", ".obj" ) end } dir.close end def makeMakefile makefile = File.new( "makefile", "w" ) # OBJ ファイル一覧 makefile.print "OBJS =" @files.each { |file| makefile.print " " + file } makefile.puts "" makefile.puts "LIBS = cppunit.lib" makefile.puts "INC = D:\\_work\\etc\\cppunit-x-20020331" makefile.puts "" makefile.puts "test : $(OBJS)" makefile.puts "\truby TestRunnerFactory.rb" makefile.puts "\tcl -GX -I$(INC) Tester.cpp $(LIBS) $(OBJS)" makefile.puts "\tTester.exe" makefile.close end end factory = TestMakefileFactory.new factory.getCppList factory.makeMakefile
実行
下記のようなバッチファイルを作成し実行する
TestMakefileFactory.rb で makefile を生成するので、nmake の前で実行する。
このとき cpp の検索で 自動生成される Tester.cpp が邪魔になるので、exe や obj もろとも "del Tester.*" で削除した。
del Tester.* ruby TestMakefileFactory.rb nmake pause
結果
この結果で以下のようなファイルが出力され実行される。
makefile
OBJS = Counter.obj Quiz.obj LIBS = cppunit.lib INC = D:\_work\etc\cppunit-x-20020331 test : $(OBJS) ruby TestRunnerFactory.rb cl -GX -I$(INC) Tester.cpp $(LIBS) $(OBJS) Tester.exe
Tester.cpp
#define CPPUNIT_COMPATIBLE #include <cppunit/TestCase.h> #include <cppunit/TestSuite.h> #include <cppunit/TestRunner.h> #include <cppunit/TestCaller.h> USING_NAMESPACE_CPPUNIT using namespace std; #include "CounterTest.h" class tmpCounterTest : public CounterTest { public: tmpCounterTest( const char* name ) : CounterTest( name ) {} static Test* suite() { TestSuite* suite = new TestSuite( "CounterTest"); suite->addTest( new TEST_CALLER( CounterTest, test_init)); suite->addTest( new TEST_CALLER( CounterTest, test_incr)); suite->addTest( new TEST_CALLER( CounterTest, test_decr)); suite->addTest( new TEST_CALLER( CounterTest, test_clear)); return suite; } }; #include "QuizTest.h" class tmpQuizTest : public QuizTest { public: tmpQuizTest( const char* name ) : QuizTest( name ) {} static Test* suite() { TestSuite* suite = new TestSuite( "QuizTest"); suite->addTest( new TEST_CALLER( QuizTest, testQuestion)); suite->addTest( new TEST_CALLER( QuizTest, testAnswer)); return suite; } }; int main( int argc, char* argv[] ) { TestRunner runner; runner.addTest( "CounterTest", tmpCounterTest::suite() ); runner.addTest( "QuizTest", tmpQuizTest::suite() ); return runner.run( argc, argv ); }
まとめ
これで以下の3ファイルを対象フォルダにぶち込んでバッチファイルを実行すれば、勝手に makefile を生成して、ビルドして、実行してくれる。
我ながらなかなかいい出来だと思う(笑)
- テスト対象.h
- テスト対象.cpp
- テストコード.h
補足
ここから拝借した Counter のテストコードは CppUnit-x 用を本家 CppUnit で動作するようにしたものなので、それをさらに CppUnit-x 用に戻す。
ついでにテストコードを cpp に書いてると CppUnit-x ヘッダをインクルードしなきゃならんのでテストコードはヘッダにまとめた。
変更した CounterTest.h を以下に示す。これにより CounterTest.cpp は使用しなくなった。
#ifndef __COUNTERTEST_H__ #define __COUNTERTEST_H__ #include "Counter.h" class CounterTest : public TestCase { public: // いつもきまった書き方のコンストラクタ CounterTest( const char* name ) : TestCase( name ) {} void setUp() { // 前準備 } void tearDown() { // 後始末 } ////////////// TEST CASES ////////////////// void test_init() { Counter c; assertEquals( 0, c.value() ); } void test_incr() { Counter c; assertEquals( 0, c.value() ); c.incr(); assertEquals( 1, c.value() ); c.incr(); assertEquals( 2, c.value() ); } void test_decr() { Counter c; assertEquals( 0, c.value() ); c.decr(); assertEquals( -1, c.value() ); c.decr(); assertEquals( -2, c.value() ); } void test_clear() { Counter c; assertEquals( 0, c.value() ); c.incr(); assertEquals( 1, c.value() ); c.clear(); assertEquals( 0, c.value() ); } }; #endif