チーム開発における単体テストの実践~Visual Studio 2012 ソリューションシナリオ
<<クロスポスト:この Blog は、「Visual Studio 日本チーム Blog」へ投降した「チーム開発における単体テストの実践~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 のライセンスホワイトペーパー」を参照ください)。
プラクティス3:既存の単体テスト資産も活用しよう
既存のプロジェクトを受け継いだり、あるいはプロジェクトの統合によって、既存のコード資産とともに、単体テストも「プロジェクト資産」として受け継ぐことがあります。
しかしそれらの単体テストの資産が、これから使おうとしている Unit Testing Framework と異なるものであったり、また、開発チームの違いから、使用している Testing Framework が異なる場合などが起こるかもしれません。
そのような場合においては、単体テストの実行においては、それぞれのフレームワークにあわせたテスト実行が必要でした。
Visual Studio 2012 では、Visual Studio に付属する Microsoft Test 以外のサードパーティ製のフレームワーク、例えばマネージコード用の NUnit や、ネイティブコード用の xUnit++ といったオープンソースの Testing Framework を利用した単体テストに関しても、「テスト エクスプローラー」で一元管理を行うことが可能です。
「テスト エクスプローラー」において、既存の単体テスト資産を表示し、テストの実行等を行うためには、それぞれのフレームワークにあわせた Adaptor を拡張機能マネージャーからインストールします。
このアダプターを利用することにより、既存の単体テスト資産の活用が容易になります。
(もちろん、開発メンバーが Microsoft Test よりも NUnit を使用した開発経験が長くプロジェクトにおいて NUnit を使いたい、といった際に、この Adoptor を活用し NUnit の実行をテストエクスプローラーから実施する、といったこともできます。)
具体的な手順については「方法: サードパーティ製の単体テスト フレームワークをインストールする」を参照ください。
また、テスト エクスプローラーにおいて、サードパーティ製の単体テスト フレームワークを活用できるVisual Stduio 2012 のエディションは「Visual Studio 2012 の機能比較」のページの「テスト ツール」の項目の「拡張テスト フレームワーク」をご確認ください。Visual Studio ライセンスに全般ついては「Visual Studio 2012 と MSDN のライセンスホワイトペーパー」を参照ください)。
プラクティス4:テストをわかりやすく整理しておこう
開発プロジェクトが進展するにつれ、単体テストのパターン、数も増加します。
適切な単体テスト実行を行うためにも、それらを適切に分類し、また必要な時に必要なテストが行えるように整理を行いましょう。
具体的な手法としては、テストの名称をわかりやすいものにしておく(テストエクスプローラーから、一目で実行すべきテストが理解できるようにしておく)、あるいは、テストプロジェクトを分割しておく、といった方法が考えられます。
Visual Studio 2012 においては、これらの手法に加え、開発者が独自の「特徴」(Traits)を設定し、テストエクスプローラーにおいて分類、あるいは検索を行うことが可能です。
※ この機能を利用するには Visual Studio 2012 の Update 1 をインストールしてください。また、Update 1 については、Visual Studio 2012 の Update 1 に関するブログを参照ください。
具体的には、以下のように Test Method 毎に「特徴」を定義します。
プロジェクトをビルド後、テストエクスプローラーにおいて「特徴」による分類を指定すると、Test Method で指定した項目に基づき、テストがグループ化され表示されます。
また、Trait の項目で、検索を行うことも可能です。
検索においては「-」(マイナス)キーワードを使用することで、除外したいキーワードを指定することもできます。
また、サードパーティ製の単体テストフレームワークを利用した場合でも、同様にグループ化を行うことが可能です。
下記は NUnit を利用した際のカテゴリー、プロパティの使用例です。
このようなコードを記述したのち、テストエクスプローラーで、Owner 情報が “toiwade” のテストを確認すると、Microsoft Test を利用したテストと、NUnit を利用したテストが同じように表示されていることが確認できます。
この状態で、テストの実行を行うことにより、Microsoft Test および NUnit を使って作成されたテストが同時に実行されます(”Owner[toiwade] ” で分類されたテスト3つのうち、メソッド名の冒頭に ”NUnit” がついているのが NUnit でのテスト。それ以外の2つが Microsoft Test によるテストです) 。
単体テストにおける、その他のサードパーティ製フレームワークを使用した場合のグループ化に関しては、「Using Traits with different test frameworks in the Unit Test Explorer」を参照してください。
なお、繰り返しになりますがこの機能を利用するには Visual Studio 2012 の Update 1 が必要です。Update 1 については、Visual Studio 2012 の Update 1 に関するブログを参照ください。
今回は、チーム開発における単体テスト実践に関するプラクティスと、Visual Studio 2012 の支援機能を紹介しました。
ソリューションシナリオには、以下のシナリオがあります。
・チーム開発における単体テストの実践(本エントリ)
それでは!