« メールからRedmineのチケットを自動登録する時の注意点 | トップページ | RedmineはCRMソフトとして使えるか?Part2~RedmineをCRMソフトとして使うためのプラグイン »

2013/07/05

テスト駆動開発に必要なノウハウ~ドライバとスタブ

テスト駆動開発は誰もが重要と考えているのに、現場ではあまり普及していない。
その原因の一つは、テストプログラムを書く技術として、ドライバやスタブという概念が普及していないことと、ドライバやスタブを実際に利用する方法のノウハウが不足しているからではないかと思っている。
考えたことをラフなメモ書き。

【元ネタ】
スタブとは 【stub】 - 意味/解説/説明/定義 : IT用語辞典

ドライバとは 【driver】 〔ドライバー〕 - 意味/解説/説明/定義 : IT用語辞典

トップダウンテストとは 【top down test】 - 意味/解説/説明/定義 : IT用語辞典

ボトムアップテストとは 【bottom up test】 - 意味/解説/説明/定義 : IT用語辞典

ビッグバンテストとは 【big bang test】 - 意味/解説/説明/定義 : IT用語辞典

アジャイル開発が重視する品質特性~保守性と移植性: プログラマの思索

テスト駆動開発による組み込みプログラミングも良い本です: プログラマの思索

【1】テスト駆動開発の最大の利点は、テスト自動化と回帰テストの組み合わせで、機能がデグレードしない保証を取りやすいこと。
第8回勉強会の感想~RedmineはCRMや情報系システムにも適用できる #RxTStudy: プログラマの思索で、@marutosijpさんの話を聞いて改めて思ったのは、RedmineがRailsやRubyのVerUpやJQueryへライブラリを移行できたのは、テストプログラムをきちんと書いて、CIツールで回帰テストしていたから。
Redmineから派生した他プロジェクト(Chiliprojectなど)は全て、RailsのVerUpに追いつけずに破綻している。

最近なら、WindowsXPやIE6が終了モードになり、Windows8やIE10へ移行する流れにあるが、OSやブラウザのVerUpで正常動作しなくなる業務アプリやWebアプリは非常に多い。
ユーザの観点では、OSやアプリ、ミドルウェアのVerUpがあっても正常動作すべきだが、開発者の観点では、機能がデグレードしない保証を取るためにかなりの工数をかける。

だから、ユーザの観点では、OSやミドルウェアのVerUpは何の価値もないから、SIとしてはデグレしない費用を取りにくい。
システムは変わらないのに、OSやブラウザなどの環境が変化してしまうことで、システムが正常動作しなくなるという矛盾。

だからこそ、テスト駆動開発の重要性は、最近のように環境が変わりやすい状況では非常に高まっていると思う。
しかし、実際の現場ではテスト駆動開発の重要性を知っているにもかかわらず、あまり実践されていない。
その理由は何故か?

最近思うのは、テスト駆動開発には、従来のプログラミング技術とは違う別のノウハウを必要とするのに、それを知らないからではないか、と思っている。
そのノウハウは、ドライバとスタブというテスト特有の技術だ。

【2】テスト駆動でプログラムを書くと、スタブを書く時が非常に多い。
つまり、トップダウンテストでプログラムを書く場合が多い。

スタブとは 【stub】 - 意味/解説/説明/定義 : IT用語辞典
トップダウンテストとは 【top down test】 - 意味/解説/説明/定義 : IT用語辞典

理由は、小さな部品を作るよりも、仕様がほぼ確定した上位モジュールをまず作り、詳細なロジックは外出しのプログラムとしてスタブ化してテストしておき、後からスタブ化したプログラムを実装していくパターンになりやすいから。

トップダウンテストでは、テスト対象プログラム◇---スタブとして構造化し、詳細なロジックはスタブとして外出ししてテストする。
Red→実装→Greenというテスト駆動の流れは、まずスタブを空実装してから詳細なロジックを一つずつ作っていくやり方が多いだろう。
オブジェクト指向設計ならば、責務中心のクラス設計になりやすいので、長くなりそうな処理は外部クラスでスタブ化しながら詳細化していけばよい。

スタブを使うトップダウンテストの利点は、仕様の振る舞いを決定する上位モジュールを早めにテストできることがある。
つまり、仕様漏れ、認識間違いを早期に検知できる利点がある。
しかし、欠点は、数の多い下位モジュール(スタブ)の検証が遅れるので、プログラミング力が劣る開発者は工数がかかってしまうこと。
そして、スタブとして使われるモックオブジェクトには特有の開発ノウハウが必要だから。

RSpec でテストを作るのに役立つ「モック/スタブ」のシンプルな説明 - 酒と泪とRubyとRailsとに書いているように、モックとは「「オブジェクトのメソッドがどう呼ばれて何を返すか」というインタフェースも含めたテストのために使う」ものであり、スタブとは「テストをスムーズに行うために「あるオブジェクトのメソッドが呼ばれたら、ある戻り値を返す」ために使う」ものであるという定義は分かりやすい。

多分、テスト駆動開発でつまずいてしまう理由の一つは、モックオブジェクトの利用ノウハウが整理されていないからだと思う。
実は、モックオブジェクトの実装方法は、Ruby・Java・C#などの各言語で全く違う。
JavaならjMockが有名だろう。
だから、使い方に慣れない人が多いのではないかと想像する。

RSpec でテストを作るのに役立つ「モック/スタブ」のシンプルな説明 - 酒と泪とRubyとRailsと

スタブとモック

モックとスタブの違い ? [lib]

JMockのインストールとEclipseでの使い方まとめ | Futurismo

【3】スタブの概念は、単体テストだけでなく、外部接続テストでも有効だ。
例えば、Webの注文システムをテストする時に、最後にクレジットカード払いの注文プロセスで、クレジットカード会社へクレジットカード番号の認証とオーソリ認証、与信チェックを依頼する処理があったとしよう。
すると、カード払いの注文テストを行うには、外部接続のテストサーバーを準備しなければ、ユーザの業務を想定したテストにならない。

そこで普通は、カード決済用のテストサーバーとテストプログラムを準備しておく。
処理としては、注文システムからカード番号などの情報をカード決済テストサーバーへリクエストで送り、テストサーバーでリクエスト情報をチェックした結果を注文システムへレスポンスで返す。
チェックした結果がOKならば、注文システムからテストサーバーへリダイレクトして、テストサーバー上でカード決済処理を実施して、最終結果を注文システムへレスポンスとして返す、というフローになるだろう。

すると、テストサーバーの役割は、ある特定のカード番号と個人情報は正常データとして受け取り、それ以外はオーソリ認証エラーなどでエラー返却する処理を持つモックオブジェクトのような仕組みを担当することになる。

カード決済を提供するカード会社では普通は上記のようなテストサーバーを提供してくれるので、自前で作る必要はほとんどない。
しかし、仕入先と発注データをやり取りするなど外部接続のシステム連携がある場合、モックオブジェクトの役割を持つテストサーバーを自前で作るしかない時もある。
その場合の開発工数はそれなりに大きいものだ。
だから、経験あるマネージャは、外部接続テストで必要な工数を別途大目に確保したり、開発工程とは別にテスト準備を事前に並行開発して進めたりする。
そうすることで、モックオブジェクトの役割を持つテストのリスクをカバーしているわけだ。

【4】事前に小さな部品を作って、それら部品を結合しながら開発していく場合、ドライバを使う時が多い。
いわゆるボトムアップテストでは、ドライバ◇---テスト対象プログラム(部品) という構造になり、ドライバを使って部品をテストする。
普通は、ドライバにたくさんのデータパターンを投入して部品の詳細ロジックを網羅するようにテストするから、DBUnitを使う時が多い。

ドライバとは 【driver】 〔ドライバー〕 - 意味/解説/説明/定義 : IT用語辞典

ボトムアップテストとは 【bottom up test】 - 意味/解説/説明/定義 : IT用語辞典

ボトムアップテストの利点は、数が多い独立性の高い下位モジュール(部品)を順にテストできるので、並行開発して納期を短縮しやすいこと。
例えば、組み込み製品のプログラム開発では、たくさんの部品をあらかじめ作ってから、ドライバを使って多くの部品を結合してテストするパターンが多いだろう。

逆にボトムアップテストの弱点は、振る舞いを決定する上位モジュールで不具合が見つかると、テスト完了した下位モジュールに手戻りが発生することだ。
だから、手間はかかるが、部品を1個ずつ作るたびにドライバを作ってはテストする方が安全だ。

ボトムアップテストを更に突き進めると、ウォーターフォール型開発で主流の結合テストであるビッグバンテストになる。
ビッグバンテストとは、単体テストが終了したモジュールを全て一度に結合してテストするやり方だ。
ビッグバンテストは、ドライバやスタブを用意する必要がないので準備工数が少ない利点はあるが、最大の弱点は、一度に全ての障害が発生するため、障害の原因特定が難しくなることだ。
WF型開発の結合テスト、システムテストでデスマーチになりやすい理由は、そこにある。

情報システム用語事典:ビッグバンテスト(びっくばんてすと) - ITmedia エンタープライズ

昔の業務系Webシステム開発でも、DB層、コントローラ層、ビュー層で開発担当を分けて、大量のプログラマが並行開発するパターンが多かった。
このパターンが失敗しやすい理由は、ビッグバンテストになるまで、誰も自分が作ったプログラムがまともに動くのを見たことがないから、リスクがずっと残されたままになることだろう。

最近は、Railsのような優れたWebフレームワークのおかげで、機能単位や画面単位に一気通貫で開発するので、トップダウンテストの方が多くなっている。

でも、ドライバを使ったテストで多いのは、受入テストだろう。
例えば、Web画面からのテストを自動化する場合、Seleniumを使う時が多いが、Seleniumはまさにドライバの役割を担っている。
同様に、FITのように、Wiki画面でデシジョンテーブルを作り、そのテストケースでテストする方法も、ドライバの役割に相当している。

コード品質を追求する: FITで解決する

Selenium IDEを使って、ChromeやIE上でテストスクリプトを実行する方法 | 品質向上ブログ

受入テストの自動化が重要と誰もが分かっているのに、なかなかテスト駆動が浸透しない理由の一つは、ドライバとなるテストプログラムの作成が現在のライブラリでは非常に難しいからだろうと思う。
ドライバを一から作るのは、スタブを作るよりももっと難しい。

更に、ドライバを作ったとしても、FITのようにたくさんのデータパターンを用意する必要があるので、テストケース作成の技法も必要とする。
Seleniumでも、ブラックボックステストで機能を網羅するにはどんなテストケースが必要なのか、というテスト作成技法を必要としている。
テスト技法である境界値分析、同値分析などの手法も必要としているのだ。

スタブやモックはだいぶノウハウが公開されてきているけれど、ドライバに関するノウハウはまだまだ足りないと思う。

【5】「ドメイン特化言語」を読んでいて気づいたことは、テスト駆動を支えるライブラリはDSLで実装されている場合が多いことだ。
ドメイン特化言語」では、FITやjMockがDSLの一例として紹介されている。

すなわち、テスト駆動で書いたテストプログラムは、ドメイン専門家でも理解できるような内容の方が良いことを意味している。
例えば、FITのようにWiki上でデシジョンテーブルの受入テストを作る作業は、プログラマでなくとも、受入テストを担当するユーザでも良い。
また、jMockの利用プログラムは、流れるようなインターフェイス、いわゆるメソッドチェーンで実装されているらしい。

テスト駆動ライブラリがDSLで実装されている場合が多いという事実は、ドライバやスタブ(モック)を使うための技術としてDSLが必須であることを示唆していると思う。
つまり、テスト駆動開発が未だに普及せず、テスト駆動開発に必須なスタブやドライバの技術が未熟な理由の一つは、DSLを駆使してテスト駆動の敷居を下げる手法が未完成なのだろう。

逆に言えば、テスト駆動開発に必要なドライバやスタブのライブラリ開発は、DSLという最近注目されている技術を使ってチャレンジしていくべき意義のある領域なのだろうと思う。
今後もその辺りを考えてみる。

|

« メールからRedmineのチケットを自動登録する時の注意点 | トップページ | RedmineはCRMソフトとして使えるか?Part2~RedmineをCRMソフトとして使うためのプラグイン »

プログラミング」カテゴリの記事

ソフトウェア工学」カテゴリの記事

Agile」カテゴリの記事

コメント

コメントを書く



(ウェブ上には掲載しません)


コメントは記事投稿者が公開するまで表示されません。



« メールからRedmineのチケットを自動登録する時の注意点 | トップページ | RedmineはCRMソフトとして使えるか?Part2~RedmineをCRMソフトとして使うためのプラグイン »