テスト駆動型開発(TDD)とグレーボックステスト
最近、テスト駆動型開発(TDDあるいはBDD)についての雑誌を買ったり、他のブログに感化されたりしたので軽い考察を。
==
実装とテストを繰り返しながら設計をすすめる、といった形でコーディングするとき、みなさんはどんな手順ですすめますか?僕はこんな感じでやってます。(僕はC言語をベースに開発することが多いので、C言語をサンプルに)
- 骨組みだけの関数を作成する
- テストスイートを作成する
- テストを実行する
- テストケースをクリアするように実装をすすめる
- テストスイートがクリアすることと、カバレッジ率を80%以上に保つ
/**
* func1(); ○△×をする関数
* 引数
* 復帰値
* 0 正常
* -1 エラー
*/
int
func1(
const char* str1,
char* ptr2,
int num3 )
{
return(-1);
}
モジュール設計に基づいた関数の骨組みだけを記述する。ただし、復帰値がvoid以外ならばエラー復帰をまず記述しておく。
- 引数チェック
- 正常系ケース
- 限界値ケース
- エラーケース
- 異常系ケース
テストスイートは複数のテストケースの集まりをあらわし、「引数チェック」「正常系ケース」「限界値ケース」「エラーケース」「異常系ケース」といったテストケースを作成する。引数チェックは、関数の引数にNULL、0、負の数、ヌル文字のポインタ(””)などを指定した場合に正しく引数エラーを返すかどうかを確認する。正常系ケースでは、尤もらしい動作をするであろう条件を指定する。最低でもひとつ、そしてコーディング後に追加するのもあり。限界値ケースは、条件の境界値(最大値、最小値)において正常な動作をすることを確認する。これは正常系ケースと兼ねてもいい。エラーケースは、尤もらしいエラーが発生する条件を指定し、エラーが発生することを確認するテストケース。異常系ケースは、今までの4群以外で、例えば非常に大きなデータや、言語上の限界値などを指定する。
これらのテストケースをまとめる際、仕様書の欠陥などを同時にレビューするのもよい。
骨組みだけの関数をコンパイルし、テストスイートにある条件&評価でテストする。僕の場合はMinUnitというマクロをベースにテストコードを自動生成して、「-fprofile-arcs -ftest-coverage」をオプションにしてコンパイル&実行する。
ちなみに、ほとんどテストケースは失敗に終わり、いわゆる「レッド」の状態。
/* file: minunit.h */
#define mu_assert(message, test) do { if (!(test)) return message; } while (0)
#define mu_run_test(test) do { char *message = test(); tests_run++; \
if (message) return message; } while (0)
extern int tests_run;
「-fprofile-arcs -ftest-coverage」をオプションにするのはあとでgcovでカバレッジ測定をするため。
対象となる関数を実装していき、テストケースが「グリーン」にしていく。このとき、実装とともに必要と思われるテストケースを追加していくのもあり。
テストケースをすべてクリアすることと同時に、ステートメントカバレッジにも着目すること。僕の場合は、目安は80%~95%を目標にする。
本当は「リファクタリング」も必要なのだが、とりあえずこんな感じで試しているところ。
==
さて、こういったテストファーストで実装していくこと得られるメリットは、というと、、、
- ユニットテストケースがグレーボックスっぽい
- 引数チェックでブラックボックスっぽい
- 仕様書の見える化、掘り起こしができる
境界値や異常系を考慮するので、グレーボックス的なアプローチができる。
引数チェックは境界値やエラー推測といったブラックボックス的なアプローチができる。
テストケースを作成するプロセスは、仕様を浮き彫りにする作業となる。
ただし、いいことずくめではないと思っていて、引数が多くなったり、複雑な処理になるにしたがって、テストケースは膨れ上がる。テストをグリーンにするために実施カバレッジは少なくとも100%にする必要もある。だから、どこかで手を打たなくちゃならない。その基準は、まだよくわからない。
| 固定リンク | コメント (0) | トラックバック (0)


最近のコメント