2012-02-22 7 views
7

を組み合わせる可能性の重複:
Using IoC for Unit Testingユニットテスト(モック)とDependecy注入フレームワーク

私はユニットテストおよび/または依存性の注入されている方法を理解する問題を持っていると思いますワーキング。私はユニットテストのためにNUnitとRhino Mocksを使い、Dependency Incection FrameworkとしてNinjectを使っています。一般的に、私はそれらの2つが完璧にフィットしますが、どういうわけか、それはちょっと複雑になり、理解するのが難しいようです。

(私は自転車に乗って、それを清潔に保つために良い例を作りようとします)。 DI /ユニットテストなし

1):
DIとの単体テスト知らず、私のコードは、そのように見えただろう - と私は幸せになるだろう:

public class Person 
{ 
    public void Travel() 
    { 
     Bike bike = new Bike(); 
     bike.Ride(); 
    } 
} 

public class Bike 
{ 
    public void Ride() 
    { 
     Console.WriteLine("Riding a Bike"); 
    } 
} 

私に乗るためにnew Person().Travel();

2)DIで:
私はその緊密な結合をしたくないので、私はインターフェイスとNinjectModuleを必要と私はちょうど必要があるだろう自転車!私はいくつかのオーバーヘッドを持っていますが、コードが読みやすく分かりやすい限り、それは問題ありません。私はちょうどバイククラスは変更されません、修正Personクラスのコードを渡します:

public class Person 
{ 
    IKernel kernel = new StandardKernel(new TransportationModule()); 
    public void Travel() 
    { 
     ITransportation transportation = kernel.Get<ITransportation>(); 
     transportation.Ride(); 
    } 
} 

私はまだちょうどで私の自転車に乗ることができます:new Person().Travel();

3)のユニットテストを考慮すると(なしDI):
ライドメソッドが正しく呼び出されているかどうかを確認するには、モックが必要です。私の知る限りでは、インターフェイスを注入するために、一般に2つの方法があります:コンストラクタインジェクションおよびセッター注入でクラスをnew Person(new Bike()).Travel();

4)DIとし、ユニット・テスト
の準備:この時間

public class Person 
{ 
    ITransportation transportation; 

    public person(ITransportation transportation) 
    { 
     this.transportation = transportation; 
    } 

    public void Travel() 
    { 
     transportation.Ride(); 
    } 
} 

、私は自転車を渡すためにニートになります。私は、たとえばコンストラクタ・インジェクションを選択します3.単体テスト(DIなし)を検討するは変更なしで仕事をしますが、new Person(kernel.Get<ITransportation>());に電話する必要があります。それを介して、私はDIからの利益を失っているように感じます。Personクラスは、何の繋がりもなく、交通がどのようなクラスのクラスであるかを知る必要はありません。また、このフォームには例2の可読性があまりないと思います。

これはどのように行われますか?それとも、他にも依存性注入と単体テスト(そして模擬)の可能性をよりエレガントに達成する方法がありますか?

(戻ってみると、その例は本当に悪いと思われます。誰もが今乗っている輸送機器の種類を知っているはずです...)

+0

依存性注入(DI)と制御反転(IoC)の定義について混乱していると思います。あなたは3番目の数字です*はDIを実装しています(あなたはコンストラクタに依存を移動しました)、2番目の図はIoC(Ninject)コンテナを使用して依存関係を解決しています。 –

+0

zapthedingbatが正しいです。 3番目の例ではDIを実行していますが、DIコンテナは使用しないでください。これは問題ありません。 DIコンテナは、DIを実行するときはオプションです。 – Steven

+1

例 "2" DIは使用しませんが、「サービスロケータ」は... –

答えて

10

一般的に私は単体テストにIoCコンテナを使用しないようにしています。モックとスタブを使って依存関係を渡すだけです。

問題はシナリオ2から始まります:ではなく、 DIです - これはservice locator (anti-)patternです。実際の依存関係注入あなたの依存関係を、好ましくはコンストラクタインジェクションを介して渡す必要があります。

シナリオ3は見栄えが良いです。これはDIであり、一般的にはあなたのクラスをアイソレーションでテストする方法もあります。必要な依存関係を渡します。テスト対象の各クラスはいくつかの依存関係しか持たないので、単体テストには完全なDIコンテナを使用する必要はほとんどありません。それぞれのスタブはスタブまたはモックでテストを実行できます。

がIoCコンテナであるが必要な場合は、テストが十分に細かくないか、あまりにも多くの依存関係があると主張します。後のケースでは、使用している依存関係のうちの2つ以上から集約クラスを作成するために、いくつかのリファクタリングがあります(セマンティック接続がある場合のみ)。これにより、最終的には依存関係の数が、あなたが快適であるレベルに落ちます。その最大数は各人ごとに異なります。私は個人的には最大でも4つにしようと努力していますが、少なくとも私はそれらを片手で数えることができ、嘲笑はそれほど負担にはなりません。

ユニットテストでIoCコンテナを使っに対する最後の重要な引数がある行動試験:どのようにテスト対象のクラスは、あなたがあなたの依存関係を完全に制御されていない場合にそれを望むように振る舞うことを確認することができますか?

おそらく、特定のアクションのフラグを設定するタイプですべての依存関係をスタブアウトすることでこれを達成できますが、これは大きな努力です。 RhinoMocksやMoqのような模擬フレームワークをに使用する方が、を確認するほうが、指定した引数で特定のメソッドが呼び出された方がはるかに簡単です。そのためには、呼び出しを確認したい依存関係を模倣する必要があるため、IoCコンテナはここであなたを助けません。

4

混乱しているものがあります。

実装3は、ユニットテストでDIフレームワークを設定する必要がないため、2より優れています。数3のテスト時

だからあなたはどうなる:

ITransportation transportationMock = MockRepository.GenerateStricktMock<ITransportation>(); 

// setup exceptations on your mock 

var person = new Person(transportationMock); 

DIフレームワークは、生産コードでオブジェクトツリーを構築する場合にのみ必要なのは何かです。テストコードでは、テストする内容を完全に制御できます。単位をテストすると、すべての依存関係が模倣されます。

また、いくつかの統合テストを行いたい場合は、実際の自転車を人のクラスに渡してテストします。

完全に独立してクラスをテストするという考えは、それぞれのコードパスを制御できるということです。依存関係が正しいか不正な値を返すようにすることもできますし、例外をスローすることさえできます。すべてが機能していて単体テストのコード範囲が充分であれば、DIが正しく配線されているかどうかを確認するために、さらに大きなテストが必要になります。

テスト可能なコードを書くための鍵は、ビジネスロジックからオブジェクトの作成を分割することです。

3

私の2セント...

ポイント2は、依存関係逆転の原則(DIP)の例であるが、それはむしろ、依存性の注入よりも、サービスロケーション・パターンを使用しています。

あなたのポイント3は、IoCコンテナがPersonのビルド中にコンストーラーに依存性(ITransportation)を注入するDependency Injectionを示しています。

実際のアプリと単体テストでは、IoCコンテナを使用してPersonを作成することもできます(つまり、新しいPersonを直接作成しないでください)。ユニットテストフレームワークがこれをサポートしている場合は、サービスロケータパターン(kernel.Get<Person>();)またはDI(たとえばセッタ)を使用してください。これは、その後、人と(ITransportationのために設定具象クラスすなわち)その依存関係を構築し、同様の人にそれを注入します

(あなたのIoCをテストユニットで、明らかに嘲笑のために設定されるだろう/ ITransportationをスタブ)

最後に、PersonのTransport()メソッドをテストできるように、モックする必要のあるDependencies、つまりITransportationです。

バイクは依存性がないため、直接/独立してユニットテストすることができます(Bikeに依存関係が追加されていない限り、Bike.Ride()をテストするためのモックは必要ありません)。

関連する問題