[突撃インタビュー]
去る8月にアメリカ・テキサス州ダラスで開催された Agile 2012 にて James Grenning さんにインタビューを実施させていただきました。James さんは、組み込みソフトウェア開発におけるアジャイル開発のコーチ・トレーナー・コンサルタント、『Test Driven Development for Embedded C』[1] の著者、アジャイルソフトウェア開発宣言の著者17名の1人、そしてアジャイルな見積り手法「プランニングポーカー」[2] の考案者でもあります。
インタビューでは、日本の「 Test Driven Development for Embedded C読書会 」参加メンバーから挙がった質問について順次尋ねる形で進めました。
2012 年 10 月号の前編に続く後編の本記事では以下の話題についてお伝えします。
・ モデリングやアーキテクチャ設計とTDDの関係
・ 従来のテストとTDDの関係
・ アジャイル開発でのシステムテストの運用
-- 書籍の中では直接モデリングについて言及していませんが、TDD を適用した開発ではモデリングを行わないのでしょうか?ただ、書籍の中で SOLID 原則 [3] について言及しており、TDD を行う中でモデリングすることを暗に勧めているように読めますが、それは正しいでしょうか?
SOLID 原則を紹介している第11章「SOLID, Flexible, and Testable Designs」の中でいくつか責務 [4] の分割に関する設計を行なっています。ここで重要な原則の一つが「関心事の分離 [5]」です。書籍では蛍光灯の点灯や消灯のスケジュールを管理をするソフトウェアを設計する中で、全体を異なる関心事の領域に分割し、システムを構築する上で必要なインタフェースを用意する場所を特定しています。例えば、ここには蛍光灯のスケジューリングに関するロジックがあって、ここはスタブに置き換えられるようにしよう、というように。そういうわけで、いつどの程度やるかを明記していないだけで、一定量のモデリングはやっているわけです。設計や実装の活動の最初にモデリングを通じて、自分がどこに向かっているのか検討することは良いことだと思います。
-- それは必要になった時にモデリングするということでしょうか?
そうです。私はモデルを「設計のビジョン」と呼ぶことが好きです。私は、設計のビジョンとして大きなシステムをどのように責務へ分割していくかを考えます。それぞれのレベルにおいて、ビジョンには7〜8個の構成要素しか含まれないように注意します。それは人間は多くの物事を扱うのが不得意だからです。まず、高いレベルで責務を分割し、さらにその責務を繰り返し、小さい責務に分割します。
書籍の中では私は大きなシステムをそのまま扱わないと説明しています。大きなシステムの全体を想像しながら、特定の部分に注意を払います。書籍の例で言えば、全体としては家庭で発生する作業を自動化することを考えるとしても、最初は蛍光灯の点灯や消灯のスケジューリングを検討しています。これは本質的には詳細を階層化する作業です。
多くの人がTDDのコード中心な側面を見て、「TDDは設計しない手法だ」と誤解しますが、実際に我々がやっているのは継続的な設計です。オブジェクトのインタフェースを定義する時はいつも、そのインタフェースを実際に使うテストを書きます。それは設計の活動です。私の書籍の例をいくつか見れば分かると思いますが、テストケースの作成によって最初にインタフェースを作ります。なので、最初の方のテストケースがインタフェース設計を駆動するのです。そして、後の方のテストケースで設計の詳細を検討します。
そして、私はコードに翻訳可能で実行可能なレベルの詳細なUMLモデルは書きません。個人的には時間の無駄だと思っています。モデルの適切な詳細さは、オブジェクトの名前、責務、そしてモジュールのいくつか代表的な属性や操作くらいです。C/C++コードのシグニチャレベルの詳細さが適切だと考えている人もいるでしょうが、私自身はそれは詳細すぎると考えています。
-- アーキテクチャ設計についてはどうでしょうか?TDDを適用した開発でもアーキテクチャ設計をやるのでしょうか?
先ほどと同じ考えです。以前、アーキテクチャ設計を行うプロジェクトに参加した時に事前に大量のモデリングを行いましたが、それは間違っていると気づきました。私は堅苦しくモデリングするより、小さいモデルを書く方が好きです。設計が実際に動くかどうかを確かめるためにモデルを書くことも良いでしょうが、その代わりに私はテストを書いて確かめるのが好きです。テストはオブジェクトがどのように動くかを明確に示してくれます。そしてインタフェースが使いやすいかも示してくれます。これは私が長年の経験の中で得た結論です。
-- 同値分割、カバレッジ分析、オールペア法などといった従来のテスト手法と TDD はどういう関係にあるのでしょうか?
TDD をやれば、他のテスト手法が不要になるということはありません。TDD はコードがプログラマの考えた通りのことをしているか確かめる手法です。そして、コードの問題を早く見つけられるようにし、そこにあったことすら気づいていなかった問題を取り除くための手法です。TDD によって自分のコードが何をしているのか分からないという状況を防いでくれます。ただし、TDD のテストはモジュールがプログラマの考えた通りに動いていることは示してくれますが、製品が仕様通りだとは言いません。製品が仕様通りか確認するためのテストは必要になります。
-- 例えば、TDD をやる時にカバレッジの目標値を設定したりはしないのでしょうか?
カバレッジは興味深いメトリックです。テストが網羅的にできているか把握する上では役に立ちますが、インセンティブとしては最悪です。なぜならカバレッジが高いが、ひどいテストというのは実際に起こり得るからです。
もし、テストがコード全体を実行していてもコードが正しいかをチェックしていなければ、何が正しいのか分からないんですから製品開発は失敗します。TDD を導入していれば 100% に近いカバレッジになるでしょうが、テストカバレッジがゴールではありません。
本質的にプログラミングは複雑で、間違いが発生しやすい作業です。プログラマが行った全ての決断に対して、プログラマの考えたとおりになっているのか検証する必要があります。私の書籍の最初の方では、非常に小さいステップで検証していくプロセスを紹介しています。注意深く丁寧に検証していく、このプロセスが結果的にはソフトウェアの品質につながります。
-- 次の質問はアジャイル開発のライフサイクルについてです。システムテストに1週間かかるような大きなシステムを開発している時、システムテストはイテレーションの中で行うべきなのでしょうか?そうすると生産性が落ちる可能性があります。あるいはライフサイクルの最後にシステムテストをまとめてやるべきでしょうか?
組み込みシステム開発において、全てのイテレーションで意味があり、価値がある機能をリリースすることは非常に大変な作業です。それはハードウェアとソフトウェアを同時にテストしなければいけないからです。
質問に対して簡潔に答えるなら、どんな新しい機能であれ、それらの機能はイテレーションの中でテストされるべきです。理想を言えば、ユーザストーリー [6] はイテレーションの中で開発し、テストし、受け入れ基準を満たすべきです。さらに受け入れ基準の中には受け入れテストが含まれるべきです [7]。
実際の大きいシステムでは、自分が加えた変更でシステムが壊れていないことを確認するために回帰テストが必要になります。回帰テストを持続可能にするには、大量の手動テストなしに壊れた箇所を発見するために回帰テストを自動化する必要があります [8]。
ただし、手動テストをゼロにして、全て自動化しろという意味ではありません。我々がやりたいことは、必要な手動テストの量を減らすことです。そして不具合の検出を受け身で待つのではなく、能動的に不具合を防ぐことです。これは実際には言うのは簡単ですが、実践することは非常に困難です。ただ、やる価値はあります。
私はテストの自動化が実際に役に立つと気づいたので皆さんにおすすめしています。バグのない状況はありませんが、問題を未然に防ぐことができます。
-- 次はシステムテストの入力に関する質問です。ユーザストーリーは機能の詳細や例外系の振る舞いをカバーしていません。そのような入力から詳細で包括的なシステムテストを実施するにはどうしたら良いのでしょうか?
例えば、要求仕様には「システムは、与えられた日時に蛍光灯を点灯したり、消灯したりする」というような文章が書かれているでしょう。非常に曖昧ですし、正確でもありません。テストするには、もっと正確な情報が必要です。
「与えられた日時に蛍光灯を点灯したり、消灯したりする」がスケジューリングに関する全てのストーリーであれば、このようなストーリーを元に与えられた日時に蛍光灯をつける機能を開発します。しかし、「与えられた日時」という曖昧な書き方ではテストできません。より明確な受け入れ基準が必要になります。
例えば、「蛍光灯を月曜日の朝8時に点灯する」という風に明確にテストケースを表現できればテストできます。「火曜日に消灯する」というテストケースであればテストできます。このように要求仕様を受け取って、テストケースでソフトウェアの仕事を詳細に表現するのです。最初のテストケースでは仕様で求められていることのうち狭い範囲をパスします。そして、テストケースが増えるにつれて設計を汎用化していきます。例えば、最初に月曜日のテストケースを作成し、月曜日にだけ動くようにします。次に火曜日のテストケースを作成し、任意の曜日に対して動く様に設計を汎用化していきます。
そういうわけで、私は設計に注意を払うというより、コードを汎用化してくれるようなテストケースを選んでいきます。テストケースを選ぶことで設計を汎用化していく過程を見れば、多くの人はTDDの設計の側面を理解してもらえると思います。テストケースがひと通り終わった時に汎用的な設計が完了しています。
おそらく、あなたの質問は「要求仕様を受け取った。さて、これからどう段階的にテストを進めていくのか?」でしょう。例えば、2週間で5つのストーリーを完了させるとします。実際にはもっとストーリーはあり、まだ開発していない蛍光灯のスケジューリングはずっと先にあるとします。最初のイテレーションでこれらのテストを完了する計画を立てます。受け入れテストがないと次のイテレーションにリリースするのが困難になるので受け入れテストも用意するでしょう。そして、実験室に行って実際のハードウェアにつないでテストできるようになります。ここがゴールです。それぞれのイテレーションの中で作業を完了していきます。
コンピュータプログラムは単に作業するだけです。人間が要求仕様を読んで、そこからソフトウェアの仕事を抽出する必要があります。この作業を段階的にやってください。そして作業を進めていく中で設計を汎用化していきます。
-- ユーザーストーリーがあったとしても、ユーザーストーリーから受け入れ基準になるようなテストケースを作り出し、イテレーションの中でテストするということでしょうか?
そうです。理想的にはイテレーションの最初の時点でテストケースが分かっているべきでしょうが、実際にはイテレーションの最後まで分かっていないこともあるでしょう。開発する中でどのような製品にするか決めていくのですから、テストケースのいくらかは自分で作る必要があります。
-- 質問は以上です。長い時間お付き合い下さいましてありがとうございました。
いえいえ、こちらこそ。Agile 2012を楽しんでいって下さい。日本の読者の方にも宜しくお伝え下さい。
James Grenningさんとは初対面であり、インタビュー前はどんな人だろうと様々な妄想を巡らしました。 例えば、マッチョマンで「そこに座れ、これからTDDブートキャンプだ!」と言うような人だろうかと妄想したのですが、幸いなことにその妄想は大外れでした。 実際に会ってみると、アロハシャツが似合う大学教授のような人でした。すなわち、大学教授のような思慮深さと相手をリラックスさせるようなフレンドリーな雰囲気を兼ね備えた人でした。 インタビューの間も、自らが考案したプランニングポーカーカードを使ったりして分かりやすく質問に回答して下さいました。 というような人だったのですが、このコメントを考える際にGrenningさんにお聞きしたい質問をもう1つ思いつきました。次回お会いする時には、ぜひこの質問をしたいと思います。
インタビューを心よく引き受けて頂いた James Grenning さん、インタビューの質問を検討していただいた日本の「Test Driven Development for Embedded C」読書会の皆様、本当にありがとうございました。読書会の主催者としてこのインタビュー記事を執筆できたことを心から嬉しく思います。本連載を通じてTDDの背後にある能動的にバグを防止する姿勢、持続可能なテストの考え方が組み込み業界にも広がるとうれしいです。
そして、「Test Driven Development for Embedded C」の翻訳本が2013年春に出版されることになりました!TDDに興味あるけど英語はちょっという方もぜひ翻訳を手に取っていただきたいと思います。翻訳本にご期待ください。
[1] 書籍の内容については2012年9月号公開の書籍紹介記事を参照。
[2] 詳しくは「プランニングポーカー・オブジェクトゲームでアジャイルゲーム! Agile 2011 Conference」を参照。
[3] Robert Martin著『アジャイルソフトウェア開発の奥義 - オブジェクト指向開発の神髄と匠の技』で紹介されているオブジェクト指向設計の原則。
[4] Rebecca Wirfs-Brock著『オブジェクトデザイン - ロール、責務、コラボレーションによる設計技法』によると、責務とは「オブジェクトが持つ動作や知識、オブジェクトが他に影響を与える主要な判断」の総称。
[5] 元々は「構造化プログラミング」でも有名なエドガー・ダイクストラが提唱した言葉。ここでいう「関心事」とは、ソフトウェアを構成する様々な要素のうち個別に着目することができ、ひとまとめに扱うことのできる概念。ソフトウェアの中心的な本体から様々な関心事を分離することで、保守性や再利用性を向上する。
[6] 機能を指すScrumやXPの用語。UMLで使われるユースケースとの意味の違いは「Martin Fowler's Blik in Japanese - ユースケースとストーリー」を参照。
[7] James氏は受け入れテストに関する論文(PDF)を自身のサイトに掲載している「Scenario Testing with Executable Use Cases」。
[8] James氏のブログエントリ「Manual Test is Unsustainable」で手動テストが持続可能でなく、自動テストが必要なことを紹介している。
©2012 OGIS-RI Co., Ltd. |
|