2011-09-16 9 views
3

私はApress ASP.NET MVC 3 bookに行って、可能な限りユニットテストを確実に作成しようとしていますが、なぜ編集が保存されないのかを試してみるとよいでしょう(this SO question参照)私はこれのための単体テストを作りたがっていました。ユニットテスト可能なクラスを設計する

私は次のクラスのための単体テストを作成する必要が出てきました

public class EFProductRepository : IProductRepository { 
    private EFDbContext context = new EFDbContext(); 

    public IQueryable<Product> Products { 
     get { return context.Products; } 
    } 

    public void SaveProduct(Product product) { 
     if (product.ProductID == 0) { 
      context.Products.Add(product); 
     } 
     context.SaveChanges(); 
    } 

    public void DeleteProduct(Product product) { 
     context.Products.Remove(product); 
     context.SaveChanges(); 
    } 
} 

public class EFDbContext : DbContext { 
    public DbSet<Product> Products { get; set; } 
} 

私はNinject.MVC3と部品番号を使用していますし、仕事をしながら前述したものの(前に、いくつかのユニットテストを作成しました本)ゆっくりと私の頭の周りを取得しています。私はすでに(うまくいけば正しく)_contextに渡すために私を可能にするために、コンストラクタメソッドを作成しました:

public class EFProductRepository : IProductRepository { 
    private EFDbContext _context; 

    // constructor 
    public EFProductRepository(EFDbContext context) { 
     _context = context; 
    } 

    public IQueryable<Product> Products { 
     get { return _context.Products; } 
    } 

    public void SaveProduct(Product product) { 
     if (product.ProductID == 0) { 
      _context.Products.Add(product); 
     } else { 
      _context.Entry(product).State = EntityState.Modified; 
     } 
     _context.SaveChanges(); 
    } 

    public void DeleteProduct(Product product) { 
     _context.Products.Remove(product); 
     _context.SaveChanges(); 
    } 
} 

をしかし、これは私が悩みを持って始めるところ...私は私が(参照EFDbContextのためのインタフェースを作成する必要があると信じています以下)ので、私はテストのための模擬レポでそれを置き換えることができますが、それはクラスDbContext上に構築されています:System.Data.Entityから

public class EFDbContext : DbContext { 
    public DbSet<Product> Products { get; set; } 
} 

と私は私の人生のためのインターフェイスを作成する方法を考え出すことはできませんそれは...私は次のインターフェイスを作成する場合は、メソッドの不足のためにエラーが発生しますクラスからであり、それはクラスではないインターフェイスだとして `EFDbContextが...

using System; 
using System.Data.Entity; 
using SportsStore.Domain.Entities; 

namespace SportsStore.Domain.Concrete { 
    interface IEFDbContext { 
     DbSet<Product> Products { get; set; } 
    } 
} 

元のソースから得たことができているように、私は「DbContext」を使用してインターフェイスを構築することはできませんthis pageの "ソースコード/ダウンロード"のencase私は上記のコードの断片で何かが見逃してしまった(または単に質問して追加します)。

私は理解しているものの限界にぶつかっていますし、検索したり読んだりすることもできません。私はこれをどのように過ぎ去ったのか分かりません。 助けてください!

答えて

6

ここでの問題は、あなたが十分に抽象化されていないということです。抽象/インターフェースのポイントは、技術に依存しない方法で行動を公開する契約を定義することです。

つまり、EFDbContextのインターフェイスを作成したが、そのインターフェイスはまだ具体的な実装であるDbSet(DbSet)に結び付けられています。

これは、DbSetの代わりにIDbSetとしてこのプロパティを公開することです。理想的には、IQueryableのような抽象的なものを公開します(ただし、これはAdd()メソッドなどを提供しません)。抽象度が高いほど、模擬するのが簡単です。

次に、あなたが依存している残りの "契約"、つまりSaveChanges()メソッドを実行します。

更新されたコードは次のようになります。

public class EFProductRepository : IProductRepository { 
    private IEFDbContext context; 

    public EFProductRepository(IEFDbContext context) { 
     this.context = context; 
    } 
    ... 
} 

public interface IEFDbContext { 
    IDbSet<Product> Products { get; set; } 
    void SaveChanges(); 
} 

しかし...をあなたが聞いている主な問題は、あなたが何をモックしようとしている、逆に(テストしようとしています/テストを避ける)?言い換えれば、何かが保存されたときのアプリケーションの動作を検証しようとしていますか、またはの実際の保存をテストしていますか。

アプリケーションがどのように動作しているかをテストしていて、実際にデータベースに保存することに気をつけなければ、私はより高いレベルのIProductRepositoryを嘲笑することを検討します。それで、あなたはデータベースに全くぶつかっていません。

オブジェクトが実際にデータベースに永続化されていることを確認したい場合は、DbContextを押して、その部分をモックしたくないはずです。

個人的には、私はこれらのシナリオが異なっていることも同じように重要であると考えて、別々ののテストを行います。それぞれ自分のアプリケーションが行うべきことをテストするものと、データベースの対話が機能します。

+0

私は(または少なくとも本は持っている)IProductRepositoryの単体テストをすでに持っていて、アプリケーションがセーブを管理する方法をテストしますが、EFProductRepostioryが動作することを確認するレベルには達しませんでした(そして本の例私はそれに何かを加えたいと思っていました。データがdbに保存されているかどうかテストするのは別のレベルのテストが好きなようですが、私は擬似レポを使用していないのであればこれを正しく実行する方法はわかりませんスクリプトを作成する/データベースを拭く...そのレベルのテストは正常ですか?それはMySQLではなく、アプリをテストしているのですか? – GazB

+0

@ Zasurus:データベース層が動作するかどうかのテストは、* integration *または* system * testingに該当する重要なステップです。ユニットテストができないことをシステムテストが捉えることができる多くの問題があります(悪いデータベース接続文字列、データベーススキーマと同期していないEFモデルなど)。一方で、あなたのリポジトリコードはとてもシンプルであるように見えますが、単体テストでは、実際にエラーが発生する可能性はほとんどなく、メンテナンスオーバーヘッドが増えていると主張しています(Joel Spolsky) – StriplingWarrior

+0

それはそのアプリケーションのための唯一のテーブルなので、私はそれがシンプルだが、プロジェクトの目的はそれが今までに生きているアプリケーションではないことを学ぶことです。私は生き生きとするアプリケーションの始まりを作ることを学んだものを使用していますが。うまくいけば、まあまあのサイズに成長し、私は本当に私はそれを実際のものに適用することができますので、私は少なくとも、このアプリでこれらを行う正しい方法の周りに私の頭を得るためにしたいと思います。 :)ライブアプリの現在のバージョンは150台以上です(したがって、MVCでの書き換えが混乱しています)ので、ユニットテストが役立つと確信しています!まもなく上記を試します。 – GazB

2

私は(私はインターフェイスに入れて)あなたの現在のコードは次のようになります推測:

public class EFProductRepository : IProductRepository { 
    private IEFDbContext _context; 

    // constructor 
    public EFProductRepository(IEFDbContext context) { 
     _context = context; 
    } 

    public IQueryable<Product> Products { 
     get { return _context.Products; } 
    } 

    public void SaveProduct(Product product) { 
     if (product.ProductID == 0) { 
      _context.Products.Add(product); 
     } else { 
      _context.Entry(product).State = EntityState.Modified; 
     } 
     **_context.SaveChanges();** 
    } 

    public void DeleteProduct(Product product) { 
     _context.Products.Remove(product); 
     **_context.SaveChanges();** 
    } 
} 

public class EFDbContext : DbContext, IEFDbContext { 
    public DbSet<Product> Products { get; set; } 
} 

public interface IEFDbContext { 
    DbSet<Product> Products { get; set; } 
} 

問題は今IEFDbContextインタフェースを実装するオブジェクトを期待しますが、このインタフェースが定義されていませんEFProductRepositoryですSaveChangesアスタリスクの間に置かれた行でメソッドが使用され、コンパイラが不平を開始します。 IEFDbContextインターフェイス上SaveChangesメソッドを定義

はあなたの問題を解決:

public interface IEFDbContext { 
    DbSet<Product> Products { get; set; } 

    void SaveChanges(); 
} 
+0

私はこれを考慮しましたが、それを行うための別の方法がなければならないと考えました! :)私はそれを行って、それが行く方法を参照してください...あなたに戻ってきます。ありがとう:) – GazB

関連する問題