web-dev-qa-db-ja.com

リポジトリパターンでEntity FrameworkのDbContext.Entryメソッドを偽造する方法

コードを単体テストするため、MVC4アプリケーションにリポジトリパターンを実装しました。コンテキストインターフェイス、偽のコンテキストを作成し、 _System.Data.Entity.DbSet_ の偽の実装を使用して、 this のコードを実行しました。

残念ながら、私の前の2つのポスター( herehere )のように、 _DbContext.Entry method_ をモックすることはできません。このメソッドを使用して、コード内のデータベースエントリを次のように更新します。

_DbContext.Entry(order).State = EntityState.Modified;
_

私はこの問題の解決策を見つけていません。

「そして、このコードを単体テストするポイントは何ですか?Findメソッドを偽造し、DbEntityEntryを偽造すると、テストする実際のロジックはありません。」

または

続行する前に this およびリンクされているすべての質問をお読みください。 (...)リポジトリをテストする場合は、実際のデータベースと通信する統合テストを作成します。

それはすべて良いことですが、それでも質問に対する答えはありません。私は批評を読みましたが、このEntryメソッドがまだ必要なので、ユニットテストで偽のコンテキストを使用し、モックオブジェクトを使用できます。もちろん、統合テストも使用しますが、いくつかの簡単な単体テストほど高速ではありません。

いくつかの実装を試みるときに受け取るエラーは、Error 2 'Project.Models.Order' does not contain a definition for 'State' and no extension method 'State' accepting a first argument of type '[whatever return type I use]' could be found (are you missing a using directive or an Assembly reference?)

誰かが偽のDbContext.Entryメソッドを作成するのを手伝ってくれることを願っています。

39

「間接レベルを追加する」ことで答えが見つかりました here

_public void SetModified(object entity)
{
    Entry(entity).State = EntityState.Modified;
}
_

コントローラーでDbContext.SetModified(entity)を使用します。

36

これを回避するために、メソッドオーバーロードを追加し、廃止された属性を追加して、元のメソッドが呼び出されている場所を確認しました。

    public virtual void Entry<TEntity>(TEntity entity, Action<DbEntityEntry<TEntity>> action) where TEntity : class
    {
        action(base.Entry(entity));
    }

    [Obsolete("Use overload for unit tests.")]
    public new DbEntityEntry<TEntity> Entry<TEntity>(TEntity entity) where TEntity : class
    {
        return base.Entry(entity);

        /** or **/

        throw new ApplicationException("Use overload for unit tests.");
    }

その後、DbContext.Entry(order, ent => ent.State = EntityState.Modified;

5
Dave

インターフェイスベースのリポジトリと作業単位を実装して、目的を達成する方法の例:

public interface IRepository<T>
    {
        T FindSingle(Expression<Func<T, Boolean>> predicate, params Expression<Func<T, object>>[] includeExpressions);
        void ProxyGenerationOn();
        void ProxyGenerationOff();
        void Detach(T entity);
        void Add(T newEntity);
        void Modify(T entity);
        void Attach(T entity);
        void Remove(T entity);
        void SetCurrentValues(T modifiedEntity, T origEntity);
        T GetById(int id);
        T GetById(int id, bool sealOverride);
        IQueryable<T> GetAll();
        IQueryable<T> GetAll(bool sealOverride);
        IQueryable<T> GetAll(string[] EagerLoadPaths);
        IQueryable<T> Find(Expression<Func<T, Boolean>> predicate);
    }



public interface IUnitOfWork : IDisposable
    {
       //repository implementations go here
       bool SaveChanges()
     }

コンテキストが完全に抽象化されていることに注目してください。具体的な実装でのみ心配する必要があります。

2
Maess