2013-02-17 119 views
3

今天我想開始單元測試一個小的asp.net MVC 3 Web(測試)應用程序來學習一些新的東西。實體框架:實現單元測試的接口

但事情發生了再厲害我預期...

我現在已經瞭解與實體框架關係的單元測試一些線程,現在我首先要實現的接口爲我的實體框架相關類,以便我可以在單元測試中實現內存「數據庫」。

我的代碼是從ASP.NET MVC tutorial。我讀過MSDN,但在我的情況下它並沒有幫助我。

我想告訴你我的代碼。我使用的工作模式的單元庫:

單位工作:

public class SqlUnitOfWork : IUnitOfWork, IDisposable 
{ 
    private SqlContext context = new SqlContext(); 
    private IGenericRepository<Message> messageRepository; 
    private IGenericRepository<Receipt> receiptRepository; 
    private IGenericRepository<Useraccount> useraccountRepository; 
    private bool disposed = false; 

    public IGenericRepository<Message> MessageRepository 
    { 
     get 
     { 
      if (this.messageRepository == null) 
      { 
       this.messageRepository = new SqlGenericRepository<Message>(context); 
      } 
      return messageRepository; 
     } 
    } 

    public IGenericRepository<Receipt> ReceiptRepository 
    { 
     get 
     { 
      if (this.receiptRepository == null) 
      { 
       this.receiptRepository = new SqlGenericRepository<Receipt>(context); 
      } 
      return receiptRepository; 
     } 
    } 

    public IGenericRepository<Useraccount> UseraccountRepository 
    { 
     get 
     { 
      if (this.useraccountRepository == null) 
      { 
       this.useraccountRepository = new SqlGenericRepository<Useraccount>(context); 
      } 
      return useraccountRepository; 
     } 
    } 

    public SqlUnitOfWork() 
    { 
    } 

    ~SqlUnitOfWork() 
    { 
    } 

    public virtual void Dispose(bool disposing) 
    { 
     if (!this.disposed) 
     { 
      if (disposing) 
      { 
       context.Dispose(); 
      } 
     } 

     this.disposed = true; 
    } 

    public void Dispose() 
    { 
     Dispose(true); 
     GC.SuppressFinalize(this); 
    } 

    public void Save() 
    { 
     context.SaveChanges(); 
    } 
} 

這一個實現我創建了一個接口。

我對SQL通用倉庫:

public class SqlGenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class 
{ 
    internal SqlContext context; 
    internal DbSet<TEntity> dbSet; 

    public SqlGenericRepository(SqlContext context) 
    { 
     this.context = context; 
     this.dbSet = context.Set<TEntity>(); 
    } 

    ~SqlGenericRepository() 
    { 
    } 

    public virtual IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> filter = null, 
     Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, string includeProperties = "") 
    { 
     IQueryable<TEntity> query = dbSet; 

     if (filter != null) 
     { 
      query = query.Where(filter); 
     } 

     foreach (var includeProperty in includeProperties.Split 
      (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) 
     { 
      query = query.Include(includeProperty); 
     } 

     if (orderBy != null) 
     { 
      return orderBy(query).ToList(); 
     } 
     else 
     { 
      return query.ToList(); 
     } 
    } 

    public virtual TEntity GetByID(object id) 
    { 
     return dbSet.Find(id); 
    } 

    public virtual void Insert(TEntity entity) 
    { 
     dbSet.Add(entity); 
    } 


    public virtual void Delete(object id) 
    { 
     TEntity entityToDelete = dbSet.Find(id); 
     Delete(entityToDelete); 
    } 

    public virtual void Delete(TEntity entityToDelete) 
    { 
     if (context.Entry(entityToDelete).State == EntityState.Detached) 
     { 
      dbSet.Attach(entityToDelete); 
     } 
     dbSet.Remove(entityToDelete); 
    } 

    public virtual void Update(TEntity entityToUpdate) 
    { 
     dbSet.Attach(entityToUpdate); 
     context.Entry(entityToUpdate).State = EntityState.Modified; 
    } 
} 

它實現了我編程接口:

public interface IGenericRepository<TEntity> where TEntity : class 
{ 
    IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> filter = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, string includeProperties = ""); 

    TEntity GetByID(object id); 

    void Insert(TEntity entity); 

    void Delete(object id); 

    void Delete(TEntity entityToDelete); 

    void Update(TEntity entityToUpdate); 
} 

我現在想實現一個「InMemoryGenericRepository」爲我的單元測試,那麼「InMemoryUnitOfWork」。 那些「InMemoryGenericRepository」將如何?

我想我會使用這個倉庫,所有數據都存儲內的泛型列表:

IEnumerable<TEntity> List { get; set; } 

但我怎麼能適應這個方法:

public virtual IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> filter = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, string includeProperties = "") 
{ 
    IQueryable<TEntity> query = dbSet; 

    if (filter != null) 
    { 
     query = query.Where(filter); 
    } 

    foreach (var includeProperty in includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) 
    { 
     query = query.Include(includeProperty); 
    } 

    if (orderBy != null) 
    { 
     return orderBy(query).ToList(); 
    } 
    else 
    { 
     return query.ToList(); 
    } 
} 

所以它的工作與我的

IEnumerable<TEntity> List { get; set; } 

我希望你做到了,直到我的問題結束。

+2

你有什麼空的析構函數?去掉它! – abatishchev 2013-02-17 21:46:01

+0

你是如何生成實體的?他們是POCO嗎? – abatishchev 2013-02-17 21:53:02

+1

空析構函數來自Enterprise Architect。必須執行它們。 :-) 模型是POCO。 – mosquito87 2013-02-17 21:54:53

回答

0

通用存儲庫模式不是一個好的模式。它會給你帶來很多痛苦,以換取存儲獨立性(你可能不需要,你喜歡擁有它,但你並不需要它)。你真的有一個很難寫一個可查詢內存庫的幾個原因:

  • 實體具有其必須建立正確的關係
  • 在內存中的查詢行爲不同於SQL查詢(例如,他們是區分大小寫)
  • 內存查詢有沒有優化,這可能會導致測試過度運行
  • 你現在來測試測試,基本上,所以你可以肯定的是,在行爲不存在差別

此外,通過抽象化您的ORM,您可以使用其功能。您只能使用最通用和最常用的功能。

幾年前,我已經完成了這一切,痛苦並沒有結束。我發現使用真正的SQL數據庫進行測試是一個很好的模式。這樣你就不需要任何存儲庫並且可以測試真實的東西。這有它的問題,但它是可行的。

此問題的解決方案是放棄通用存儲庫模式。

+0

是否可以使用實體框架,但在我的單元測試中使用框架? – mosquito87 2013-02-18 07:25:42

+0

什麼框架?通用知識庫?當然有可能。你必須解決我提到的所有問題,這不僅是一次工作,而且是一個持續的工作。如果企業架構師要求做到這一點,請問他如何以合理的方式克服所有這些問題。 – usr 2013-02-18 08:27:44

+0

Enterprise Architect不是真人,而是軟件。 感謝您的關注。我會考慮這一點。 我目前正在考慮嘲笑我用Entity Framework實現的圖層。 – mosquito87 2013-02-18 08:45:59

1

如果類POCO然後,他們正在尋找這樣的:

namespace Project.Model // auto-generated Foo.cs 
{ 
    public partial class Foo // notice partiality 
    { 
    } 
} 

然後你這樣寫:

namespace Project.Model // custom Foo.cs in another folder 
{ 
    public partial class Foo : IEntity // notice interface 
    { 
    } 
} 
+1

您也可以編輯T4模板並將您的界面添加到生成的類。 – MiBu 2013-02-17 22:05:35

+1

@MiBu:對。但是當由於某種原因你需要從接口實現中排除一個實體或者實現一個更具體的接口時,你會再次編輯T4來添加一些hack或者像這樣添加部分類。 – abatishchev 2013-02-17 22:08:33

0

你也可以用重寫你的懶惰特性:

private IGenericRepository<Message> messageRepository; 

public IGenericRepository<Message> MessageRepository 
{ 
    get 
    { 
     return messageRepository ?? (messageRepository = new IGenericRepository<Message>()); 
    } 
} 

private Lazy<IGenericRepository<Message>> messageRepository = new Lazy<IGenericRepository<Message>>(new IGenericRepository<Message>())); 

public IGenericRepository<Message> MessageRepository 
{ 
    get 
    { 
     return messageRepository.Value; 
    } 
}