2009-07-23 11 views
2

私が読んで、質問ユニットテスト動的ローディングコード

public List<T> LoadPlugin<T>(string directory) 
{ 
    Type interfaceType = typeof(T); 
    List<T> implementations = new List<T>(); 

    //TODO: perform checks to ensure type is valid 

    foreach (var file in System.IO.Directory.GetFiles(directory)) 
    { 
     //TODO: add proper file handling here and limit files to check 
     //try/catch added in place of ensure files are not .dll 
     try 
     { 
      foreach (var type in System.Reflection.Assembly.LoadFile(file).GetTypes()) 
      { 
       if (interfaceType.IsAssignableFrom(type) && interfaceType != type) 
       { 
        //found class that implements interface 
        //TODO: perform additional checks to ensure any 
        //requirements not specified in interface 
        //ex: ensure type is a class, check for default constructor, etc 
        T instance = (T)Activator.CreateInstance(type); 
        implementations.Add(instance); 
       } 
      } 
     } 
     catch { } 
    } 

    return implementations; 
} 

にaswerとしてこのコードを発見し、それは私がこのコードは次のようになり、ユニットテストにどのような最善の方法を思っていましたか?

答えて

1

その1つの方法では、3つの無関係なことが行われます(SRP参照)。それぞれのクラスは、独自のクラスに分けられていなければなりません。これは、インターフェイスを実装しているため、テスト容易性のためにモックできます。ツリーのものは次のとおりです。

  1. プラグインディレクトリから.dllファイルを見つける。

  2. .dllをロードし、それに含まれる型を取得する。これは、APIメソッドを呼び出す1つのライナーでなければなりません。プログラミング言語のライブラリが正しく動作することを合理的に仮定できるので、これを実際にテストする必要はありません(少なくとも単体テストではありません)。

  3. プラグインタイプのインスタンスを作成する。

アルゴリズムは、これらの三つの部分に分離されたら、ユニットテスト部品隔離1及び3缶(C#がない限り、それは、ファイルシステムに触れるので、パート1のための技術的試験は、ユニットテストではないもののJava 7のNIO2ファイルシステムAPIのように、ファイルシステムを模擬するための何らかの方法があります。パート2を嘲笑して、それらをまとめたコードをユニットテストすることもできます。

0

ダイナミックアセンブリの検出と読み込みを抽象化し、その部分を模擬して単体テストの一部としてテストアセンブリを読み込むことができます。

ディレクトリを指定できるので、単体テストコードの一部として、コンピュータのTEMPディレクトリに一時ディレクトリを作成し、ユニットテストプロジェクトから単一のアセンブリをコピーして、プラグインシステムは、そのディレクトリをスキャンします。それをこのようにリファクタリングすることで

+1

ユニットテストでファイルシステムを使用することはお勧めできません) –

+1

これは依存しています。個人的に私は単体テストを他のものと同じように宗教的なものと見なしておらず、他の人が傍観しているものを喜んで利用します。しかし、私が言わなければならないことは、可能な限りファイルシステムを遠ざけることが私の最初の選択であろうということです。 –

4

public List<T> LoadPlugin<T>(Type[] types) 
{ 
    Type interfaceType = typeof(T); 
    List<T> implementations = new List<T>(); 

    //TODO: perform checks to ensure type is valid 
    try 
    { 
     foreach (var type in types) 
     { 
      if (interfaceType.IsAssignableFrom(type) && interfaceType != type) 
      { 
       //found class that implements interface 
       //TODO: perform additional checks to ensure any 
       //requirements not specified in interface 
       //ex: ensure type is a class, check for default constructor, etc 
       T instance = (T)Activator.CreateInstance(type); 
       implementations.Add(instance); 
      } 
     } 
    } 
    catch { } 

    return implementations; 
} 
0

あなたが使っているどのユニットテストフレームワーク言いませんでしたので、私はここでのVisual Studioの組み込み機能を仮定します。

ユニットテスト作業ディレクトリにコピーするには、問題のアセンブリをデプロイメント項目のリストに追加する必要があります。

私が見る問題は、すべての異なる実装でユニットテストを実行していることです。すべての実装を単一のテストでテストすると、どの実装が失敗しているのかが不明確になります。実装ごとにテストを1回実行します。

しかし、これを行うためにデータ駆動型のテストメカニズムをひどく悪用する可能性があります。すべてのx実装(Class_Initializeなど)をロードした後に、いくつかのテンポラリデータベーステーブルに数値0 ... x-1を挿入することができます。 各テストごとにそのテーブルを設定すると、テストは入力データとして数字でx回実行されます。それをあなたのimplementationsリスト(メンバー変数に格納されている)へのインデックスとして使用し、その実装でテストを実行してください。

はい、非常に醜いです。醜さを増やすことなく実際のデータ駆動テストを行う能力がなくなります。

1

私は内部ループ本体をメソッドに抽出します。私はそのメソッドがTインスタンスを返すようにするか、テストに失敗した場合はnullを返します。今、単体テストを書くことができます。

if (interfaceType.IsAssignableFrom(type) && interfaceType != type) 
    return (T)Activator.CreateInstance(type); 
else 
    return null; 

今、私は私がnull以外のインスタンスが返されることを期待しているため、この新しい機能の種類を供給することができ、そして私がnullのリターンを期待する種類れます。残りのコードはすべてシステムコールを使用しているようですが、私はそれらを信頼する傾向があります。しかし、あなたがしなければ、それらを孤立してテストしてください。 GetFiles()が正しいファイルリストを返すことをテストします。 GetTypes()が特定のファイルなどに適切なタイプを与えることをテストしてください。

関連する問題