2012-06-14 5 views
5

これは実際の質問よりも解決策/回避策です。私はスタックオーバーフローまたは実際に多くのグーグルの後にこの解決策を見つけることができなかったので、私はここにそれを掲示しています。EFコードによるユニットテストFirst DataContext

問題:

私は、私はユニットテストを書きたいという最初のEF 4コードを使用してMVC 3 Webアプリケーションを持っています。私はNCrunchを使用して、私がコードしているようにオンザフライでユニットテストを実行するので、ここで実際のデータベースにバックアップすることは避けてください。

その他のソリューション:

IDataContext

私はこれにメモリのDataContextに作成するための最も受け入れ方法を見つけました。これは、MyDataContext用のインタフェースIMyDataContextを作成し、そのインタフェースをすべてのコントローラで使用することを効果的に含みます。これを行う例はhereです。

これは私が最初に行ったルートですが、私は重複した依存コードを維持する必要がないので、MyDataContextからIMyDataContextを抽出するためのT4テンプレートを作成しました。

しかし、私は、MyDataContextの代わりにIMyDataContextを使用すると、いくつかのLinqステートメントが実動で失敗することをすぐに発見しました。具体的にこれは実際には非常に簡単だったこの非サポート例外をスロー

var siteList = from iSite in MyDataContext.Sites 
       let iMaxPageImpression = (from iPage in MyDataContext.Pages where iSite.SiteId == iPage.SiteId select iPage.AvgMonthlyImpressions).Max() 
       select new { Site = iSite, MaxImpressions = iMaxPageImpression }; 

マイソリューションよう

を照会します。私は単にMyDataContextにMyInMemoryDataContextサブクラスを作成し、すべてのIDbSet <をオーバーライド..>プロパティ以下の通り:

public class InMemoryDataContext : MyDataContext, IObjectContextAdapter 
{ 
    /// <summary>Whether SaveChanges() was called on the DataContext</summary> 
    public bool SaveChangesWasCalled { get; private set; } 

    public InMemoryDataContext() 
    { 
     InitializeDataContextProperties(); 
     SaveChangesWasCalled = false; 
    } 

    /// <summary> 
    /// Initialize all MyDataContext properties with appropriate container types 
    /// </summary> 
    private void InitializeDataContextProperties() 
    { 
     Type myType = GetType().BaseType; // We have to do this since private Property.Set methods are not accessible through GetType() 

     // ** Initialize all IDbSet<T> properties with CollectionDbSet<T> instances 
     var DbSets = myType.GetProperties().Where(x => x.PropertyType.IsGenericType && x.PropertyType.GetGenericTypeDefinition() == typeof(IDbSet<>)).ToList(); 
     foreach (var iDbSetProperty in DbSets) 
     { 
      var concreteCollectionType = typeof(CollectionDbSet<>).MakeGenericType(iDbSetProperty.PropertyType.GetGenericArguments()); 
      var collectionInstance = Activator.CreateInstance(concreteCollectionType); 
      iDbSetProperty.SetValue(this, collectionInstance,null); 
     } 
    } 

    ObjectContext IObjectContextAdapter.ObjectContext 
    { 
     get { return null; } 
    } 

    public override int SaveChanges() 
    { 
     SaveChangesWasCalled = true; 
     return -1; 
    } 
} 

この場合、私のCollectionDbSet <>は単にIDbSetを実装>here FakeDbSet <(を少し変更したバージョンです基になるObservableCollectionとObservableCollection.AsQueryable()を使用して)。

このソリューションは、私のすべての単体テスト、特に、これらのテストを実行中のNCrunchでうまく動作します。

完全統合は、これらのユニットテストは、すべてのビジネスロジックをテストするが、1つの大きな欠点は、あなたのLINQ文のどれもが、あなたの実際のMyDataContextでの動作が保証されていないということです

をテストします。これは、メモリ内のデータコンテキストに対するテストが、Linq-To-Entityプロバイダを置き換えていることを意味します(Linq-To-Objectsプロバイダ(this SOの質問への回答で非常にうまく指摘されています)。

これを解決するには、ユニットテストでNinjectを使用し、ユニットテストでMyDataContextの代わりにInMemoryDataContextをバインドするように設定します。統合テストを実行するときに、Ninjectを使用して実際のMyDataContextにバインドすることができます(app.configの設定を使用)。

if(Global.RunIntegrationTest) 
    DependencyInjector.Bind<MyDataContext>().To<MyDataContext>().InSingletonScope(); 
else 
    DependencyInjector.Bind<MyDataContext>().To<InMemoryDataContext>().InSingletonScope(); 

ご意見がありましたらお知らせください。ただし、常に改善が必要です。

+1

[ここ](http://stackoverflow.com/questions/4128640)はこれに関するスタックオーバーフローの質問です。[こちらの記事はこちら](http://www.cuttingedge.it/blogs/steven/pivot/) entry.php?id = 84)、この問題の別の解決策を説明しています。 – Steven

+1

[こちら](http://stackoverflow.com/questions/10967921/decouple-ef-queries-from-bl-extension-methods-vs-class-per-query)は、同じトピックについてのもう一つの最近の質問です。 –

答えて

3

私の質問によると、これは他人がこの問題を探しているのに役立つものです。しかし、質問の下のコメントに指摘されているように、この問題を解決する他のいくつかの設計アプローチがあります。

関連する問題