11

Linq-to-Entities(EF4)とLinq-to-Objectsの潜在的な違いにより、実際のデータベースを使用してクエリクラスがデータを取得する必要がありますEFから正しく。 SQL CE 4はこれには最適なツールだと思われますが、いくつかの問題に直面しています。これらのテストはMsTestを使用しています。Sql CE 4データベースを機能テストに使用する方法

問題は、データベースがモデル変更のために再作成されない場合、各テスト後にデータを削除せずにデータベースにデータが追加され続けることです。これにより、テストで競合が発生する可能性があり、クエリより多くのデータが返されます。

最初の考えはメソッドのTransactionScopeを初期化し、トランザクションをTestCleanupに配置することでした。残念ながら、SQL CE4はトランザクションをサポートしていません。

私の次のアイデアは、TestCleanupのデータベースをFile.Delete()コールで削除することでした。残念ながら、これは、最初のテストが実行された後、最初のテストのTestCleanupがデータベースを削除するようだが、最初のテスト後のすべてのテストはデータベースを再作成しないように見えるので、データベースファイルが見つかりません。

私は私のテストクラスのClassInitializeClassCleanupTestInitializeTestCleanupタグを変更しようとしましたが、それは前ClassInitializeに実行されているため、テストにNullReferenceExceptionでエラーが発生した(あるいはそれが表示されます。ClassInitializeはので、多分それはだ基底クラスでありますそれを引き起こす)。

Sql CE4をテストに効果的に使用する方法がなくなりました。誰か良いアイデアはありますか?


編集:私は解決策を見つけ出しました。私のEFユニットのテスト基底クラスでは、自分のデータコンテキストの新しいインスタンスを開始し、次に context.Database.Delete()context.Database.Create()を呼び出します。ユニットテストは少し遅くなるが、今、私はユニットテスト効果的に実際のデータベース


最終編集使用することができます前後にMicrosoftと一部のメールの後を、それが TransactionScope sが今SQLCEで許可されていることが判明しますSqlCEの最新リリースでリリースされました。ただし、EF4を使用している場合は、トランザクションを開始する前に明示的にデータベース接続を開く必要があるという点でいくつかの制限があります。あなたの TestInitializeあなたが次のことを行う必要がありますで

[TestMethod] 
    public void My_SqlCeScenario() 
    { 
     using (var context = new MySQLCeModelContext()) //ß derived from DbContext 
     { 
      ObjectContext objctx = ((IObjectContextAdapter)context).ObjectContext; 
      objctx.Connection.Open(); //ß Open your connection explicitly 
      using (TransactionScope tx = new TransactionScope()) 
      { 

       var product = new Product() { Name = "Vegemite" }; 
       context.Products.Add(product); 
       context.SaveChanges(); 
      } 
      objctx.Connection.Close(); //ß close it when done! 
     } 
    } 
+0

もちろん、SQL CEはトランザクションをサポートしていますが、TransactionScopeを使用するのは非常に間違った方法です。通常は、Connectionオブジェクトを介して行います。 – leppie

+0

あなたが 'SaveChanges()'を呼ばないことを意味しない限り、 'TransactionScope'を持たないEF4エンティティではどうしたらいいのか分かりません。つまり、テストは有効なテストではありません。 – KallDrexx

+0

Sql CEでデータをシードする方法の例を挙げることができますか?私はEF6を使用して、それをテストしたいと思っています。 –

答えて

4

次のコードが正常にユニット/機能テスト用のSQL CEを使用する方法のサンプルを示し

System.Data.Entity.Database.DbDatabase.SetInitializer<YourEntityFrameworkClass>(
    new System.Data.Entity.Database.DropCreateDatabaseAlways<YourEntityFrameworkClass>()); 

これは、エンティティフレームワークは常に再作成するようになりますテストが実行されるたびにデータベース。

なお、DropCreateDatabaseAlwaysから継承する別のクラスを作成することもできます。これにより、毎回、設定されたデータでデータベースをシードできます。私は「最後の編集」のアプローチは、同様に私の作品見つけ

System.Data.Entity.Database.DbDatabase.SetInitializer<YourEntityFrameworkClass>(
    new DataContextInitializer()); 
+0

最終的にこれをチェックする機会を得た後、これはうまくいかないようです。テストを実行する前にデータベースを削除しているようですが、各テストを個別に実行する前にデータベースを削除しているわけではありません。これは、ユニットテスト全体でデータを持続させ、いくつかの他のテストが失敗する原因となっています。私は今のところそれを回避することができますが、私はむしろそれなしで動作します。 – KallDrexx

+0

Nevermind - それはあまりに複雑すぎて単体テストにはうまくいきません...基本的には複数のテーブルを単一のエンティティにマップするために使用し、その特定のテーブルを呼び出すたびに新しいアセンブリで実行されます。実際に説明するのは難しい:) – Buildstarted

+0

各テストの後にテーブルをクリアして、シードメソッドでそれらを再設定できませんでしたか?または、それぞれ異なるテストセットをテストしていますか? – Buildstarted

3

public class DataContextInitializer : DropCreateDatabaseAlways<YourEntityFrameworkClass> { 
    protected override void Seed(DataContext context) { 
     context.Users.Add(new User() { Name = "Test User 1", Email = "[email protected]" }); 
     context.SaveChanges(); 
    } 
} 

は、その後、あなたの初期化中に、あなたはへの呼び出しを変更します。しかし、それは本当に迷惑です。これは単なるテスト用ではなく、Entity FrameworkとSQL CEでTransactionScopeを使用したいときに便利です。私は一度コードを作成し、SQL ServerとSQL CEの両方をサポートするようにしたいと思いますが、どこでもトランザクションを使用する必要があります。確かに、Entity Frameworkチームがこれを処理しているはずです!

その間、私は自分のコードでそれを少しきれいにするために、それをさらに遠ざけました。 (あなたはDbContextから派生どんなクラス)あなたのデータコンテキストにこのブロックを追加します:あなたが実際にそれを使用する場合

public MyDataContext() 
{ 
    this.Connection.Open(); 
} 

protected override void Dispose(bool disposing) 
{ 
    if (this.Connection.State == ConnectionState.Open) 
     this.Connection.Close(); 

    base.Dispose(disposing); 
} 

private DbConnection Connection 
{ 
    get 
    { 
     var objectContextAdapter = (IObjectContextAdapter) this; 
     return objectContextAdapter.ObjectContext.Connection; 
    } 
} 

これは、多くのクリーナーます:

using (var db = new MyDataContext()) 
{ 
    using (var ts = new TransactionScope()) 
    { 
     // whatever you need to do 

     db.SaveChanges(); 
     ts.Complete(); 
    } 
} 

私はあなたを設計する場合と仮定しますがすべての変更がSaveChanges()への1回の呼び出しでコミットされるようなアプリであれば、暗黙的なトランザクションで十分です。テストのシナリオでは、ts.Complete()を呼び出す代わりにすべてをロールバックしたいので、確かに必要です。私は、利用可能なトランザクションスコープが必要な他のシナリオがあると確信しています。 EF/SQLCEが直接サポートしていないのは残念です。

+0

興味深い解決策。私はちょうど私の 'IUnitOfWork'クラスが' BeginTransaction() 'と' EndTransaction(bool commit) 'メソッドを持つ回避策を行いました。 'BeginTransaction'がまだ存在しない場合は接続を開き、' EndTransaction'はトランザクションを簡単に完了またはロールバックすることができます。今のところこれもうまくいくようです。うまくいけば、私はトランザクションスコープを単独で使うことができます。 – KallDrexx

関連する問題