C#とRhinoMocksを使用したテスト駆動開発のベストプラクティス[終了]


86

チームがテスト可能なコードを作成できるようにするために、C#コードベースをよりテストしやすくするためのベストプラクティスの簡単なリストを作成しました。(いくつかのポイントは、C#のモックフレームワークであるRhino Mocksの制限に言及していますが、ルールはより一般的に適用される場合もあります。)従うベストプラクティスはありますか?

コードのテスト容易性を最大化するには、次のルールに従います。

  1. 最初にテストを記述し、次にコードを記述します。理由:これにより、テスト可能なコードを記述し、コードのすべての行でテストが記述されるようになります。

  2. 依存性注入を使用してクラスを設計します。理由:見えないものをモックしたりテストしたりすることはできません。

  3. Model-View-ControllerまたはModel-View-Presenterを使用して、UIコードをその動作から分離します。理由:テストできない部分(UI)を最小限に抑えながら、ビジネスロジックをテストできるようにします。

  4. 静的メソッドまたはクラスを記述しないでください。 理由:静的メソッドを分離することは困難または不可能であり、RhinoMocksはそれらをモックすることができません。

  5. クラスではなく、インターフェイスをプログラムします。理由:インターフェースを使用すると、オブジェクト間の関係が明確になります。インターフェイスは、オブジェクトがその環境から必要とするサービスを定義する必要があります。また、RhinoMocksやその他のモックフレームワークを使用してインターフェイスを簡単にモックすることができます。

  6. 外部の依存関係を分離します。理由:未解決の外部依存関係はテストできません。

  7. モックするメソッドを仮想としてマークします。理由:RhinoMocksは非仮想メソッドをモックできません。


これは便利なリストです。現在、NUnitとRhino.Mocksを使用しています。ユニットテストのこちら側にあまり詳しくないチームメンバーには、これらの基準を詳しく説明することをお勧めします。
クリスバラード

回答:


58

間違いなく良いリストです。ここにそれに関するいくつかの考えがあります:

最初にテストを記述し、次にコードを記述します。

私は、高いレベルで同意します。しかし、もっと具体的に言うと、「最初にテストを作成してから、テストに合格するのに十分なコードを作成して、繰り返します」。そうでなければ、私の単体テストは統合テストや受け入れテストのように見えるのではないかと心配しています。

依存性注入を使用してクラスを設計します。

同意しました。オブジェクトが独自の依存関係を作成する場合、それらを制御することはできません。制御の反転/依存性注入はその制御を提供し、モック/スタブなどでテスト対象のオブジェクトを分離できるようにします。これは、オブジェクトを分離してテストする方法です。

Model-View-ControllerまたはModel-View-Presenterを使用して、UIコードをその動作から分離します。

同意しました。プレゼンター/コントローラーでさえ、スタブ/モックのビューとモデルを渡すことにより、DI / IoCを使用してテストできることに注意してください。詳細については、Presenter FirstTDDをご覧ください。

静的メソッドまたはクラスを記述しないでください。

私はこれに同意するかどうかわかりません。モックを使用せずに静的メソッド/クラスを単体テストすることが可能です。だから、おそらくこれはあなたが言及したRhinoMock固有のルールの1つです。

クラスではなく、インターフェイスをプログラムします。

同意しますが、理由は少し異なります。インターフェイスは、さまざまなモックオブジェクトフレームワークのサポートだけでなく、ソフトウェア開発者に大きな柔軟性を提供します。たとえば、インターフェイスなしではDIを適切にサポートすることはできません。

外部の依存関係を分離します。

同意しました。インターフェースを使用して、(必要に応じて)独自のファサードまたはアダプターの背後に外部依存関係を非表示にします。これにより、Webサービス、キュー、データベースなど、外部の依存関係からソフトウェアを分離できます。これは、チームが依存関係(別名外部)を制御していない場合に特に重要です。

モックするメソッドを仮想としてマークします。

これがRhinoモックの制限です。モックオブジェクトフレームワークよりも手書きのスタブを好む環境では、それは必要ありません。

そして、考慮すべきいくつかの新しいポイント:

創造的なデザインパターンを使用します。これはDIに役立ちますが、そのコードを分離して、他のロジックとは独立してテストすることもできます。

BillWakeのArrange / Act / Assertテクニックを使用してテストを作成します。この手法により、必要な構成、実際にテストされているもの、および期待されるものが非常に明確になります。

自分のモック/スタブを転がすことを恐れないでください。多くの場合、モックオブジェクトフレームワークを使用すると、テストが非常に読みにくくなることがわかります。自分でローリングすることで、モック/スタブを完全に制御でき、テストを読みやすくすることができます。(前のポイントに戻って参照してください。)

単体テストからの重複を抽象基本クラス、またはセットアップ/ティアダウンメソッドにリファクタリングする誘惑を避けてください。そうすることで、単体テストを実行しようとする開発者から構成/クリーンアップコードが隠されます。この場合、重複をリファクタリングするよりも、個々のテストの明確さが重要です。

継続的インテグレーションを実装します。すべての「緑色のバー」でコードをチェックインします。ソフトウェアを構築し、すべてのチェックインで単体テストの完全なスイートを実行します。(確かに、これはそれ自体がコーディングの慣習ではありませんが、ソフトウェアをクリーンで完全に統合するための素晴らしいツールです。)


3
私は通常、テストが読みにくい場合、それはフレームワークのせいではなく、テストしているコードのせいであることに気づきます。SUTの設定が複雑な場合は、さらに多くの概念に分割する必要があります。
スティーブフリーマン

10

.Net 3.5を使用している場合は、Moqモックライブラリを調べることをお勧めします。これは、式ツリーとラムダを使用して、他のほとんどのモックライブラリの直感的でないレコード応答イディオムを削除します。

このクイックスタートをチェックして、テストケースがどれほど直感的になるかを確認してください。簡単な例を次に示します。

// ShouldExpectMethodCallWithVariable
int value = 5;
var mock = new Mock<IFoo>();

mock.Expect(x => x.Duplicate(value)).Returns(() => value * 2);

Assert.AreEqual(value * 2, mock.Object.Duplicate(value));

5
Rhino Mocksの新しいバージョンもこのように機能すると思います
George Mauer


3

これは非常に役立つ投稿です!

コンテキストとテスト対象システム(SUT)を理解することが常に重要であることを付け加えておきます。既存のコードが同じプリンシパルに従う環境で新しいコードを作成する場合は、TDDプリンシパルをレターに従わせる方がはるかに簡単です。しかし、TDD以外のレガシー環境で新しいコードを作成していると、TDDの取り組みが見積もりや期待をはるかに超えてすぐに膨らむ可能性があることがわかります。

完全にアカデミックな世界に住んでいる一部の人にとって、タイムラインと配信は重要ではないかもしれませんが、ソフトウェアがお金である環境では、TDDの取り組みを効果的に活用することが重要です。

TDDは、限界収穫逓減の法則の対象となります。要するに、TDDに向けたあなたの努力は、最大のリターンのポイントに達するまでますます価値があり、その後、TDDに投資されたその後の時間はますます価値がなくなります。

TDDの主な価値は、境界(ブラックボックス)と、システムのミッションクリティカルな領域のホワイトボックステストにあると私は信じがちです。


2

インターフェイスに対してプログラミングする本当の理由は、Rhinoの作業を楽にするためではなく、コード内のオブジェクト間の関係を明確にするためです。インターフェイスは、オブジェクトがその環境から必要とするサービスを定義する必要があります。クラスは、そのサービスの特定の実装を提供します。Rebecca Wirfs-Brockの役割、責任、協力者に関する「オブジェクトデザイン」の本を読んでください。


同意しました...それを反映するために質問を更新します。
ケビンアルブレヒト

1

良いリスト。あなたが確立したいと思うかもしれないことの1つ-そして私はそれについて自分で考え始めたばかりなので私はあなたに多くのアドバイスを与えることができません-クラスが別のライブラリ、名前空間、ネストされた名前空間にあるべきときです。ライブラリと名前空間のリストを事前に把握し、チームが会って2つをマージするか、新しいものを追加することを決定する必要があることを義務付けることもできます。

ああ、あなたもやりたいと思うかもしれない私がすることを考えただけです。私は通常、各テストが対応する名前空間に入るクラスポリシーごとのテストフィクスチャを備えた単体テストライブラリを持っています。私はまた、よりBDDスタイルの別のテストライブラリ(統合テスト?)を持っている傾向があります。これにより、メソッドが何をすべきか、そしてアプリケーションが全体的に何をすべきかを特定するためのテストを書くことができます。


また、個人プロジェクトで(単体テストコードに加えて)同様のBDDスタイルのテストセクションを実行します。
ケビンアルブレヒト

0

これが私がやりたいと思ったもう一つのことです。

TestDriven.NetやNAntからではなく、単体テストのGuiからテストを実行する場合は、単体テストのプロジェクトタイプをライブラリではなくコンソールアプリケーションに設定する方が簡単であることがわかりました。これにより、テストを手動で実行し、デバッグモードでテストを実行できます(前述のTestDriven.Netで実際に実行できます)。

また、私はいつもPlaygroundプロジェクトを開いて、慣れていないコードやアイデアをテストするのが好きです。これはソース管理にチェックインしないでください。さらに良いことに、開発者のマシン上の別のソース管理リポジトリにのみ存在する必要があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.