テストデータのセットアップ
Seasarのからさわぎ@大阪行ってきました.設計手法「くーす」の話.以下感想.
- 一番最初にUIモック(画面プロトタイプ)を作れってのはいい.オブジェクト指向だと具体的な画面UIをなるべく排除するのがいいとされているが,そうするとユーザはついてこれない.
- ロバストネス分析使えそう.でも用語はイヤ(バウンダリ,コントロール)
- シナリオ分析かなりだるそう.やってられない.
- 業務ロジックがStatelessということは,シナリオベースではなくメソッドベースのテストケースを書いてる?
- 完璧なインターフェイス仕様を求めてるところはウォーターフォールっぽい?
以下,あんまり関係のないこととか
- くーすって沖縄のお酒の名前?
- きむきむさん=オレジュさん?
- はぶさんのホワイトボードによるフォローうまい.ひがさんといいコンビですな.
- 景品(Perl/CGI本)欲しいと思ったの僕ぐらい? 場違いPerlプログラマって僕だけ?(泣).
まあそんな感じ.くーすは機会があればぜひ使ってみようと思ったんですが,ただ受け入れられないことが....
自分Excelなんか嫌いですから!!! 残念!!!
ひがさんのVBA使ってくださいっていう言葉に頭くらくらしました(T_T).
というわけで,自分がくーす使うとしたらこんなふうにカスタマイズ.
- Excelで書く部分はXML(+RubyでHTMLに出力)に変更
- シナリオ分析はRubyスクリプトに変更
(シーケンス図:RubyUnitテストケース,インターフェイス仕様:Rubyのクラス定義)
あるいはシナリオ分析はしょっていきなりJUnit+POJOでテストコード書き始めるかもしれないな.
さて,からさわぎでは説明がなかったが,SeasarではテストデータをExcelに準備してテストコードが書けるらしい.
僕の場合は,もちろんExcelは使わないし,別ファイルに書いたりもしない.テストデータは直接テストコードに書き込む派だ.ただ,テストデータをうまく埋め込まないと冗長でわかりにくいテストコードなってしまう.そこで,どんなふうにテストデータをセットアップしているか紹介しよう(但しPerlUnit).
まずDBテストを行うためのテストケースを定義する(SampleDBTestクラス):
package SampleDBTest; use base qw(Test::Unit::TestCase); use Test::Unit::Sql; sub set_up { my $self = shift; $self->sql_set_up; } sub tear_down { my $self = shift; $self->sql_tear_down; } ...
このクラスはTest::Unit::TestCaseの子クラスだが,Test::Unit::Sqlを宣言している(Test::Unit::Sqlは自作&非公開).このように書かれたテストケースはDBテスト用メソッドが使えるようになる(但し上のようにset_up, tear_downで個別にsql_set_upとsql_teardownメソッドを呼ぶ必要あり).
まず一番よく使うのがsave_tableメソッド:
my $employee = $self->save_table('employee');
save_tableメソッドはその名の通り,employeeテーブルを保護するメソッドだ.テスト側でemployeeテーブルをどういじってもマスターのテーブルには影響しない(DBのテスト参照).
また,このメソッドはTableオブジェクトを返す.Tableオブジェクトのinsertメソッドでレコードを追加していくことができる:
$employee->insert(emp_no => 1, dept_cd => '10', name => '従業員1'); $employee->insert(emp_no => 2, dept_cd => '10', name => '従業員2');
これでemployeeレコードを2件追加したことになる.名前つき引数でレコードのフィールド名と値を指定する.もしNULLを設定したいならundefでOKだ:
$employee->insert(emp_cd => 999, dept_cd => undef, name => '幽霊従業員');
このメソッドが使いやすいのは,明示的に指定のないフィールドにはダミー値が設定されることだ.これによってNOT NULL制約がたくさん書かれたテーブルでも簡単にテストデータを用意できる.
さらに,こんなこともできてしまう:
$employee->insert(emp_no => [10, 11, 12]); $employee->insert(emp_no => [(20 .. 29)]); $employee->insert(emp_no => [40, 41], dept_cd =>['40', '41']);
このコードでは,フィールド値に配列を指定している.こうすると配列が展開された件数分だけレコードが追加される.つまり,1行目はemp_noが10, 11, 12の3レコードを追加,2行目は20〜29までの10レコードを追加,3行目は各配列の直積,つまり(emp_no, dept_cd) = (40, '40'), (40, '41'), (41, '40'), (41, '41')の4レコードを追加する.集計データのテストを書くときに便利だ.
他に名前つき引数のいいところは,デフォルトパラメータを別に用意できることだ.具体的には次のようになる:
my %default_params = (emp_type => 1, update_time => '2004/09/12'); $employee->insert(emp_no => 100, %default_params); $employee->insert(emp_no => 200, %default_params); $employee->insert(emp_no => 300, %default_params, emp_type => 2);
こうするとemp_typeが1,update_timeが2004/09/12のデータをデフォルトで設定できる.但し,最後のemp_no:300のレコードはemp_type:2で上書きしている(後書き優先).Perlではこんなことも可能だ.これでテストデータはかなり簡略化できる.
以上で,save_tableメソッドの説明は終わりだが,この他にsave_load_tableメソッドがある.このメソッドは,CSVデータをレコードにインサートするメソッドだ:
$self->save_load_data( company => " 01,会社1 02,会社2 03,会社3 " dept => " 01,1a,部署1a 01,1b,部署1b 02,2a,部署2a 03,3a,部署3a " );
のコードでは,companyテーブルに3件のレコード,deptテーブルに4件のレコードをCSV形式でインサートする(Perlでは文字列中に改行しても大丈夫).上のコードは次と同じだ:
$self->save_data('company'); $self->save_data('dept'); $self->load_data( company => " 01,会社1 02,会社2 03,会社3 " dept => " 01,1a,部署1a 01,1b,部署1b 02,2a,部署2a 03,3a,部署3a " );
テーブルのカラム数が少ないマスタデータなどはsave_load_dataを利用すればよい.load_table, save_load_tableメソッドの問題点は,CSVにすべてのフィールド値を列挙しないといけないことだ.スキーマの仕様変更に弱いため注意する必要がある.
最後に,execute_sqlというメソッドがある.これはSQL文をそのまま発行するメソッドだ.
$self->execute_sql(" update employee set name = '新従業員名1' where emp_no = 1; update employee set name = '新従業員名2' where emp_no = 2; ");
これまでのメソッドでカバーしきれない部分はこのメソッドを利用すればよい.
テストデータのセットアップコードを書くポイントは,特殊解をあらわにするメソッドを用意するということだ.これはJUnit実践講座にも書いた.テストに無関係な部分は極力テストコード上に出ないようにし,関係のあるデータだけ浮き上がってくるように心がける.そうすればわかりやすいテストコードが書けるようになるだろう.
| 固定リンク
トラックバック
この記事のトラックバックURL:
http://app.cocolog-nifty.com/t/trackback/1414739
この記事へのトラックバック一覧です: テストデータのセットアップ:
コメント
Perlの本もらって嬉しかった人です。今の仕事でCGIを量産しているので。
C#やRubyの記事を大変参考にさせてもらってます。いらっしゃってるのを知ってたら御礼の一言ぐらい言いたかったのですが(泣。飲み会の席でまさーるさんの名前が出てたのですが、Seasarと結びつきませんでした。
DBのテストデータですが、私は、DataSetのMockObjectを作るのがだるかったので(あと偽者でテストになるの?と思っていた)、データアクセス層以外のTestCaseでもDB初期化が必要になってしまい、テーブル毎の初期化Insert・Delete文を直書きしたXMLファイルを読み込んでSetUpで実行してました。
当時NUnitConverterみたいなのを思いつけば、もっと楽だったのですが...orz
ところで、外部キー制約はどうしてます?
(1)そもそもテスト用DBには設定しない。
(2)SetUpで解除、TearDownで再設定。
(3)律儀に複数テーブルにテストデータを設定。
私は(1)ですけど、これでいいのかなぁ?と不安です。
投稿者: babie (September 12, 2004 09:45 PM)
おお,同士がいたわけですね.お会いできなくてすみません.
外部キー制約ですが…
(0)そもそも本番用DBには設定しない。
です(苦笑).Viewやストアドプロシージャも基本的に作らないですね.
DBをかしこくしないといけないほど複雑なシステム担当してないからかな.
babieさんの場合は(1)で十分ではないでしょうか?
投稿者: masarl (September 13, 2004 10:56 AM)
お返事ありがとうございます。
>(0)そもそも本番用DBには設定しない。
おぉ、剛毅な......すいません、私、嘘つきました。
本当は(0)と(3)の中間してました。(外部キー制約つけたりつけなかったり...)
でも、良く考えると(1)(2)って本番・受け入れテスト時は違う例外が上がってくるんで、データアクセス層のUnitTest意味ないじゃん...orz
やるなら(0)か(3)のどちらかですね。
Excelは、VBAスクリプトのバージョン管理とUnitTestが簡単にできるなら(syntax,semanticsには目を瞑って)なんとか我慢できるんですけど。
WSH|(Perl|Ruby)+win32ole使ってもいいですが、やっぱプレーンテキストじゃないとなにかと面倒ですねぇ。
投稿者: babie (September 14, 2004 12:10 AM)
そうですね,仕様の一部なら外せないでしょうね.いい加減なこと書いてしまいました.
Excelはいろいろつらい思い出がありまして…f(^^;.もちろん便利だと思う場面ではExcel使います.でも開発文書には向いてないですね.
投稿者: masarl (September 14, 2004 11:19 PM)