[Team System|TDD] テスト駆動開発を試してみよう(2)
第一回の続きです。
ステップ6. LoanPayment() メソッドを作成しよう
さて、BaknAccount クラスをもう一度確認してみましょう。クラス ダイアグラムがいいですね。開いてみると 預金処理(DepositMoney())と払い戻し処理(MakePayment())がありますが、ローンの返済処理がありませんね。
ちなみに、この銀行では、払い戻し時に預金残高以上の額を引きだしたら自動的にローンを借りることになります。正確にはローンの返済は預金処理で可能です。というかローンがあると勝手に返済するロジックになっていますので・・・。
これからローンの返済処理 LoadPayment() というものを作成していきます。まずは、BankAccount クラスのクラスダイアグラム上に、このメソッドが存在していないことを確認しておいてください。コードと同期されてますので、当然コード上にもこのメソッドは(コメントアウトされているものを除いて)存在していません。疑り深いからはコードでも確認してみてください(^^;
ここで、クラス ダイアグラム上や、BankAccount.cs 上にメソッドを実装してしまったら、今までと同じ普通の開発手法になってしまいます。
テスト駆動開発では、プロダクションのコードを記述する前にテストのコードを記述します。
ステップ7. LoanPayment() をテストするためのテストコードを記述する
BankAccount.Test プロジェクトの BankAccountTest.cs を開いてください。ここにまず LoanPayment() のテストコードを記述してしまいます。今回は、コード中にコメントアウトされていますので、そちらを解除してみてください。
LoanPayment のテスト・レシピ① というところを見つけてコメント解除してください。
※上のスクリーンショットだと、ハイライトしているところだけを解除すれば済みます。
このテストメソッドの中身を軽く確認してください。ここでは詳細説明は省略します。ポイントは、target.LoanPayment() という BaknAccount クラスのメソッドを実行しているところです。また、このコードを見ていただければ、これから作成する予定の LoanPayment() という処理の仕様(使われ方)をコードで記述しているだけであることもわかりますね。
当たり前ですが、クラスやそのメソッドというのは使われてなんぼです。使われないクラスやメソッドはいらないわけです。当然使われ方も仕様として確定できるわけですからそれをコードで書いてしまえばいいわけです。
コードで書くと白黒はっきりします。コードって正直ですので、仕様をすべてコードとして書くと仕様上の矛盾点などもおのずと浮き彫りになってきますしね。
仕様はまだない(開発者が仕様を確定し、実装する)という場合は、使われ方をイメージする、使ってほしいその思いをコードに載せる(^^)なんて思って書くといいですね。
今回は、コメント解除という方法をとりましたが、現実的には、テストクラスやテストメソッドをスクラッチから記述していただくことになります。それでは生産性上あまり好ましくはありませんので、Visual Studio が持っている機能を活用しましょう。
ひな型となるようなテストコードがある場合は、テンプレート化するのがお勧めです。テンプレートは、[ファイル]:[テンプレートのエクスポート] で行えます。こういうケースの場合は、項目テンプレートで十分ですね。定義したテンプレートは、新しい項目を追加するときにマイテンプレートとして表示されます。
もう一つの方法はコード スニペットを用意することですね。個人的なお勧めはこちらになります。1/21 に開催された Visual Studio 2008 Ready Day の私のセッションでもこちらでご覧いただきました。
(ご希望があればコード スニペットもダウンロードいただけるようにしようかと思います)
ステップ8. テストを実行してみる(ビルドエラー)
では、実行してみましょう。テストの実行は、テストメソッド内(今回は LoanPaymentTest_通常処理ケース1() の中)で右クリックし、「テストの実行」を選択してください。
テストコード上からの右クリックによるテストの実行は、Visual Studio 2008 からの新機能になります。コーディングの延長で簡単にテスト実行が行えるようになりました。また、ショートカットキーも新たに実装されていますので、Ctrl+R, T でも同じことができます。
実行すると、まずビルドエラーになります。そうです!LoanPayment() なんて BankAccount クラスには存在していないんです。だからエラーになって当然ですね。
コンパイルエラーですので、青の波下線で表現されています。
ステップ9. メソッドを作成しましょう(メソッドスタブの生成)
target.LoanPayment() (上記のスクリーンショットの箇所です)を実行している箇所を見つけて、右クリックし、「メソッド スタブの生成」を選択してください。
そうすると、BankAccount クラスに LoanPayment() が追加されます。この状況は、プロダクションコードである BankAccount.cs でも、クラス ダイアグラムでも確認できます。
ステップ10. テストを実行してみる(失敗)
さてさて、テスト対象のメソッドも作成できたので、もう一度テストを実行してみましょう。手順はステップ8. と同じなので省きます。
すると今度はテストが失敗します。
ここで、テストが失敗する事実は大きな意義があります。テスト駆動開発では Red-Green-Refactor と表現されますが、まずテストが失敗することを確認するのは重要になります。これはまだ実装していないコードに対して、テストが成功してしまうということは、そのテストの信頼性が低いということになってしまうからです。
ステップ11. LoanPayment() を実装する
そこで、LoanPayment() の実装を行うことになります。今度は、BankAccount プロジェクトの BankAccount.cs を開いてください。
ここでは、ステップ9. で自動生成された LoanPayment() にご自身で、このテストが成功することを目的に実装をしていただいても構いませんし、自動生成されたものを削除し、コメントアウトされている「LoanPayment のレシピ①」の部分をコメント解除していただいても構いません。
先ほど記述したテストが通ればいいので、極端ですが、上のスクリーンショットのようにしてもいいわけです。体裁などにあまりとらわれずに成功することに集中しましょう。
ステップ12. テストを実行してみる(成功)
もう一度、テストを実行してみましょう。実施方法は、ステップ8. の通りです。今後は成功するはずです(だって、成功するように書いたんですから...)。
ステップ13. リファクタリング
本当はここでリファクタリングを・・・と行きたいところですが、今回は直書きしているので、そのままにしておきます。
ステップ14. 次の仕様をテストコードで書く、そしてテスト実行(失敗)
さて、次は、新しい仕様を新たなテストメソッドとして記述します。今回は、コメントを解除する形としましょう。「LoanPayment のテスト - レシピ②」という箇所を探し [TestMethod()] 以下のコメントアウトを解除します。中身を見ていただければわかりますが、先ほどのテストメソッドとの差異は、paymentAmount(ローン返済額)の値のみです。
次に、テストを実行します。この際に、今コメントアウトを解除したテストメソッドだけをテスト実行しても構いませんし、いっそのこと、このテストクラスに属するテストメソッドをすべて実行しても構いません。
まとめてテストする場合は、クラス内(class BankAccount {} の中かつ、テストメソッドの {} の外)で「テストの実行」を行います(厳密には LoanPayment() のテストだけではなく、DepositMoney() のテストも実施されてしまいますが、今回はよしとしましょう。テストしたいテストメソッドを細かく選択したい場合は、テストビュー内にテストの一覧が表示されますので、そちらから選択し、実行すると楽です)。
結果は、失敗となります。理由は、_currentLoan の値が常に 30.00F になるように実装しているためですね。
ステップ15. LoanPayment() のさらなる実装、そしてテストの実行(成功)
今度は、2つめのテストが失敗してしまっているので(1つめのテストは成功してますね)、この二つのテストが成功するようにコーディングを行います。
今回は、BankAccount.cs のレシピ①の部分をコメントアウトまたは、削除し、かわりに「LoanPayment のレシピ②」の箇所をコメントアウトの解除を行ってください。
またテストコード(BankAccountTest.cs)に戻って、二つのテストを実行してみましょう。今度は、二つのテストともに成功するはずです。
ステップ16. テスト記述、テスト失敗、コード記述、テスト成功を繰り返す
あとは、同じ流れを繰り返します。テストコード(BankAccountTest.cs)の「LoanPayment のテスト - レシピ③」の箇所のコメントアウトを解除します。今度のテストは、ローンの残高以上に返済してしまった場合になります。この仕様だと、返済額が多すぎる場合は、ローンを完済し、残った額を預金残高に回すという結果になるようになっています。
テストを実行すると、失敗します。BankAccount.cs 上の LoanPayment() では単純に、ローン残高から引数の額(返済額)を差し引きしているだけですから。
次に今まで同様に、BankAccount.cs の LoanPayment() のレシピ②の箇所をコメントアウトするか削除をし、次に「LoanPayment のレシピ③」の箇所をコメントアウト解除します。これで返済しすぎた場合のロジックに変わりましたので、三つのテストを実行するとすべてが成功することになります。
今回は、まともなリファクタリングをしていないケースでした(^^; 本来は、テストが成功した後にリファクタリングをして、コードの整形などを行い、可読性・保守性・パフォーマンスなどの観点でブラッシュアップします。
この際にも Visual Studio Team System の機能が役に立ちます。コード分析を行うことで、コーディングのプラクティスに準拠しているのかチェックができます。チェックすることで命名規則だけではなく、保守性やパフォーマンスなどの観点からもよいコードになるようなガイドを示してくれます。
コード メトリックスを用いることで、コードの現状を数値として把握することもできます。クラスの依存関係、継承の深さやコード行、サイクロマティック複雑度、そして保守容易性インデックスとして 0 から 100 の範囲で評価してくれます。これはあくまで現状把握のものであると思ってください。決して 100 になるようにするものではありません(100に近い方が保守容易性としていいのは決まっていますが)。保守が容易かどうかは、それが将来のリスクになると思ってください。または、これから変更するときの覚悟を図るものと思ってもいいですね。複雑なコードは変更したときにどこに依存するのかわからない部分があります。そういったこともみつつ、複雑度を軽減できるのならばこの段階でリファクタリングで軽減するといいですね。
リファクタリングにはもちろん、Visual Studio の機能が使えます。
最後にもう一度テストを行えば、全部成功していれば、よいということになります。失敗したらリファクタリングの過程で問題が発生したことになります。
何かコードに変更を加えたら、その影響度合いを都度テストを実行することで確認ができるため、常にコードの信頼性をキープしながら開発ができる非常に理想的な開発手法になっていることを体感頂ければと思います。
ちなみに、Visual Studio Team System のコード カバレッジ機能も併用することで、テスト実行後に、そのテストでコードのどれくらいの範囲をカバーしきれているのかを容易に把握することができます。
なお、テスト駆動開発で開発を行った場合は、コードのカバレッジは基本的には常に 100% になるはずです。なぜなら、毎回そのテストが成功(厳密にはそのテストの前までのテストがすべて成功)するためのコーディングをしているからです。もし仮に、カバレッジされていないコードがあるとすれば、それは仕様にないコーディングをしている可能性があるということになってしまいます(過剰な実装)。仕様として十分であると仮定するならば、そのコードはコメントアウトしても問題がないということになります。
仕様が足りない場合は、足りない仕様をテストコードで記述し、カバレッジされなかったところがカバーされればよしとすることもできますが、お作法としてはちょっと違う気がしますね。
といった感じで各ステップに余談なども入れたため長文となってしまっていますが、適時飛ばしながら操作しながら読んでいただければと思います。
ちょっと感覚をつかんでいただけたなら、まず実践してみてください。そしてチームの皆さんとディスカッションしてください。
ながさわともはる
Comments
Anonymous
January 21, 2008
こんにちは。MSDN オフラインセミナー 全国ツアーも先日の高崎で、本年度は一段落しました。大阪→東京→名古屋→高崎と廻ってきましたが、ありがたいことにすべての会場で満席御礼!!非常に多くの方がチーム開発や開発者による品質の作り込みの関心を寄せていいることがよーくわかりました。Anonymous
January 21, 2008
第一回 の続きです。 ステップ6. LoanPayment() メソッドを作成しよう さて、BaknAccount クラスをもう一度確認してみましょう。クラス ダイアグラムがいいですね。開いてみると 預金処理Anonymous
January 21, 2008
Cool post ... one question ? how do you get this cool pictures with cutted edges ?? thanks in advanceAnonymous
April 16, 2008
こんにちは、2日間にわたり皆さんのご参加で非常に “熱い” イベントとなった the Microsoft Conference 2008 (東京会場)、無事に走り抜けることができました。ご参加いただいた方、残念ながらご参加いただけなかった方も含め、皆さん、本当にありがとうございました。