チーム開発における単体テストの実践(その1)~Visual Studio 2012 ソリューションシナリオ
<< ”Visual Studio 2012 ソリューションシナリオ" では、開発現場における様々な課題を Visual Studio 2012 によってどのように解決できるのかを紹介いたします。>>
ソフトウェア開発において、「開発者テスト」とも位置づけられる「単体テスト(Unit Test)」は、Java における JUnit、.NET 開発における NUnit、また Visual Studio 2005 から提供されている Microsoft Test、といった様々な Unit Testing Framework の登場により、開発現場において広く受け入れられるようになってきました。
また一方で、Test Driven Developmenr (TDD)に代表される方法論の浸透により、単体テストは単なる「テスト」という位置づけではなく、「設計」ツールの一部としても活用されるようになりました。
通常のコードに加えてテストを書く必要のある単体テストの実践は、一見すると、書くべきコードが増え、生産性および保守性の両面でマイナスの影響を与えるように思われがちですが、実際には洗練された設計とコード変更に対する自信をもたらし、開発者に多くのポジティブな影響をもたらしています。
このソリューションシナリオでは、チーム開発における単体テスト実践の際にどのようなプラクティスがあり、また Visual Studio 2012 の機能をどのように活用できるのかを紹介します。
単体テストは手軽にできるテストである反面、チーム開発においては以下のような課題が発生しがちです。
- チーム開発においてはメンバー間でのテストに対する「レベル感」の違いがあり、テストの抜け、漏れが起こったり、あるいは必要以上のテストを用意してしまい余計な工数を消費してしまう。
- 現場においては、新規のコードに新規のテストを書く、というだけでなく、既存のコードに対する単体テストのニーズや、異なる Testing Framework によって構築されている既存の単体テストの活用等の課題が発生する。
- 開発が進むにつれ、コードとともに管理を行う必要のあるテストコードが増えてしまい、その管理自体が手間になってしまう。
数多くの単体テストを用意していても、変更を行った際に、その変更に対応する単体テストが用意されていなかったり、膨大なテストケースに埋もれて、必要な単体テストが活用されなければ、単体テスト実践の効果は得ることは難しくなってしまうのです。
このような問題を解決し、チーム開発における単体テストの実践をより良いものにするためには、以下のようなプラクティス(行動)が挙げられます。
具体的な共通の数値目標で単体テストの実装レベルを把握しよう。
チーム開発における単体テストの実践においては、どの程度の単体テストを書くのか、といった「レベル感」の調整が重要です。これについては例えばチームの指針として、「データアクセスクラスに関してはすべてのロジックを例外パターン含めてテストする」、「ビジネスロジックにおいては、個々のロジックの単体テスト、ならびに単体テストを組み合わせたシナリオテストまで行う」、といった目標設定が考えられます。
一方で、実際にその指針が守られているのか、あるいは期待通りのテストが書かれているのかを確かめるためには、他のメンバーが実装コードならびにテストコードを理解しながらレビューを行う必要があり、すべてのコードに関してこのようなチェックを行うのは現実的ではありません。
そこで、単体テストにおいては、「具体的な共通の数値目標」を設定し、その数値を達成するテストが用意されているかどうかを確認するようにしましょう。また、数値の設定に関しては、プロジェクトの冒頭に決定するだけでなく、実際のプロジェクトの進捗やチームメンバーのスキルを勘案しつつ、見直す機会を持ちましょう。
既存のコードにも単体テストを書こう。
開発の現場においては、新規のコード作成だけでなく、既存のコードの保守も重要な業務の一つです。
ある調査によると、IT予算のうち実に7割の予算が既存のコードの保守に費やされている、というデータがあります。また、新規の開発においても、効率化のために過去の同様のプロジェクトで作成したクラスライブラリを活用することもあるでしょう。
そのような場合に、新規のコードに対して新規の単体テストを書くだけではなく、既存のコードに対しても単体テストを用意し、プロジェクト全体のコードの品質管理を行うことが重要です。自信をもって既存のコードを活用するためにも、しっかりと単体テストを用意しましょう。
既存の単体テスト資産も活用しよう。
既存コードに対して、単体テストの資産が存在する場合はその活用を検討しましょう。
開発現場では、開発プロジェクトの進展に伴って、複数の開発チームが個別に担当していたサブシステムを統合し、より少数のメンバーでソフトウェアの保守、運用を行うことも発生しがちです。もし、それぞれの開発チーム個々に、個別の Testing Framework を使用し、単体テストの構築を行っていたとしても、今後のプロジェクト運営においては、そそれらの「単体テスト」の活用を図ることが重要です。
既存のコードだけでなく、既存の単体テストも貴重な「プロジェクト資産」として活用しましょう。
実施が必要なテストをわかりやすく整理しておこう。
せっかく用意した単体テストも適切に実施されなければ宝の持ち腐れとなってしまいます。
ビルドの検証テスト(Build Verification TEst。BVT)として、ビルドサーバーなどにおける定期的なビルドにおいて、プロジェクトに存在するすべての単体テストを実施する、という手法もありますが、細かな個別の変更に対して、すべての単体テストの実施を行うのは高コストになってしまいます。
開発プロジェクトの進展に伴い、単体テストのパターン、数が増えていきますが、それらを適切に分類し、また必要な時に必要なテストが行えるように整理を行いましょう。
Visual Studio 2012 では開発チームがこれらのプラクティス実践をスムーズに行えるように、様々な支援機能を提供しています。
プラクティス1:共通の数値目標を定めよう
単体テストにおけるもっとも基本的な数値目標は、テストのカバレッジ率です。
Visual Studio 2012 においては、単体テストの実施時に、実行された単体テストによってどの程度のロジックがカバーされたかを計測、表示するための機能が用意されています。
この機能を使うと、単体テストの実行後に「コードカバレッジの分析」メニューを選択することで、実際のコードにおいてどこがカバーされたかのビジュアル表示(カバーされたコードには青色の背景色が、カバーされていないコードには赤色の背景色がつけられます)。これにより、開発者は単体テストによるカバレッジ率を上げるためには、どのようなテストパターンが必要なのかをビジュアルに理解することができます。
単体テストにおけるカバレッジ率の表示
(コード カバレッジを使用した、テストされるプロジェクトのコード割合の確認より)
また、合わせてカバレッジ率を数値情報によっても表示します。これによって、カバレッジ率が低いコード、ブロックを効率的に発見することが可能です。
この機能を活用することで、プロジェクトにおいて、「データアクセスクラスに関してはすべてのロジックを例外パターン含めてテストする」、といった取り決めを行った際にも、実際にその目標が達成できているのかどうかを、特別な手間をかけることなく数値を使って確認することが可能になります。
また、このカバレッジ測定の機能は、Team Foundation Server のビルド機能と合わせて活用することが可能です(この場合、ビルドサーバーに対して、コードカバレッジの機能を持つ Visual Studio 2012 をインストールしておきます)。
ビルドサーバーでは、ビルドを実行後、指定されたテストを実施し、そのテストによってカバーされたカバレッジ分析の結果をビルドの結果とともに保存します。
チームメンバーは、その情報を共有することにより、チーム全体での単体テストの実施率や、テスト自体の品質に関する共通認識を持つことが可能になります。
コード カバレッジ機能の使用イメージは「Code Coverage」のビデオ(英語)をご参照ください。
コード カバレッジ機能を利用できるVisual Stduio 2012 のエディションは「Visual Studio 2012 の機能比較」のページの「テスト ツール」の項目をご確認ください。Team Foundation Server のビルドサーバーにおいて Visual Studio をインストールし、ビルドにおいてコードカバレッジ測定機能を使用する場合などに必要となるライセンスについては「Visual Studio 2012 と MSDN のライセンスホワイトペーパー」を参照ください)。
プラクティス2:既存のコードにも単体テストを書こう
「開発者テスト」として単体テストが認知され、普及するにしたがって、新規のコードに対する単体テストの実施率は高まってきています。一方で、既存のコードに対しては十分な単体テストが用意されていないことがあります。
このような場合には既存のコードに対しても単体テストを用意し、新規のコードと同様に変更に強い(コードの変更を行っても、単体テストによって変更による影響リスクを把握しやすい)状況に持っていくことが理想です。
しかしながら、既存コードの単体テストを用意するために、既存コードを変更しなければならない、しかし、それは行いたくない、といったケースも存在します。
例えば、以下のようなコードを考えましょう。
このコードでは、2000年1月1日になると、アプリケーション例外を発生させる、という機能が実装されています。
例えば、このコードに対して単体テストを書いたとしても、DateTime.Now が “2000年1月1日” を返さない限り、このロジックのカバレッジ率を 100% にすることはできません。
Visual Studio 2012 に用意された Fake Framework を使用すると、このようなコードのテストにおいて、既存のコード(この場合、Y2KChecker クラス)変更を行うことなく、System.DateTime のふるまいを “偽装” するロジックを追加したテストを作成することが可能です。
具体的には、以下のようなコードを用意します(この例では、Fake Framework における “Shim” (シム) と呼ばれる機能を利用しています)
上記のコードでは、「Y2K用レガシーコードにおける例外発生パターンのテスト」メソッドにおいて、Fake Framework によって、System.DateTime が 2000年1月1日を返すように設定しています。また、メソッドの冒頭の [ExceptedException] 属性により、この単体テストの実行時に、例外が発生することを期待している(例外が発生すれば、単体テストとしては成功である)という定義を行っています。
実際に、このようなコードを書く為には、Visual Studio 2012 のソリューションエクスプローラーで既存のコード(Y2KChecker) に対するテストプロジェクトで、偽装を行いたいライブラリに対して “Fake アセンブリ” を追加するだけでOKです(今回は System.DateTime おふるまいを偽装したいので System アセンブリに対して、Fake を追加しています)。
この操作により、以下のようなライブラリが作成され、テストプロジェクトに追加されます(既存のコードに影響がないところがポイントです)。
用意した2つのテストを実施し、カバレッジ結果を確認すると、以下のように、既存のコードに対して、変更を加えることなくカバレッジ 100% の単体テストが用意できたことがわかります。
このように、Visual Studio 2012 の Fake Framework を利用することで、既存のコードに対して変更を行うことなく、単体テストを効率的に用意することが可能になります。
Fake Framework 機能の使用イメージは「Fakes and Stubs」のビデオ(英語)をご参照ください。
Fake Framework 機能を利用できるVisual Stduio 2012 のエディションは「Visual Studio 2012 の機能比較」のページの「テスト ツール」の項目をご確認ください。また Visual Studio ライセンスについては「Visual Studio 2012 と MSDN のライセンスホワイトペーパー」を参照ください)。
本ソリューションシナリオは後編「チーム開発における単体テストの実践~その2~」に続きます。
ソリューションシナリオには、以下のシナリオがあります。
それでは!
(岩出)