2011-09-16 53 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

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

System.Data.Entity,我不能爲我的生活工作如何創建一個接口它...如果我創建以下界面,我會因爲缺少方法而出錯這是從DbContext類,我不能建立使用「的DbContext」之類的`EFDbContext接口是因爲它是一個類而不是一個接口...

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

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

原始來源可以從可得this page包裝上的「源代碼/下載」我錯過了上述代碼片段中的某些內容(或者只是詢問我將添加它)。

我已經達到了我所理解的限度,無論我尋找還是閱讀,我似乎都無法解決我如何克服這個問題。 請幫忙!

回答

6

這裏的問題是,你有沒有足夠的抽象。抽象/接口的要點是定義一個以技術無關的方式暴露行爲的合約。

換句話說,您爲EFDbContext創建了一個接口是一個很好的第一步,但該接口仍然與具體實現--DbSet(DbSet)綁定在一起。

對此的快速解決方法是將此屬性作爲IDbSet而不是DbSet公開。理想情況下,你公開的東西更像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(); 
} 

但是...你要問的主要問題是:你想測試(反過來是什麼,什麼是你想模擬出/避免測試)?換句話說:您是否試圖驗證您的應用程序在保存內容時的工作方式,還是您正在測試實際保存

如果你只是測試你的應用程序的工作方式,而不關心實際存儲到數據庫,我會考慮在更高層次上嘲笑--ProductRepository。然後你根本沒有打數據庫。

如果你想確保你的對象實際上被持久化到數據庫,那麼你應該擊中DbContext並且不想嘲笑那個部分。就個人而言,我認爲這兩種情況都是不同的 - 同樣重要 - 我寫單獨的測試每個人:一個測試,我的應用程序做它應該做的事,另一個測試數據庫交互起作用。

+0

我已經(或至少該書已經)已經得到了IProductRepository的單元測試,並測試應用程序如何管理保存,但它沒有去檢查EFProductRepostiory工作的水平(和書中的例子不!),所以我想爲此添加一些東西。測試數據保存到數據庫這似乎是我自己喜歡另一個級別的測試,我不知道正確的方式去做這個,因爲我猜如果我不使用模擬回購我將需要使用創建/擦除數據庫的腳本...是否正常的測試級別?這不是測試MySQL而不是應用程序!? – GazB

+0

@Zasurus:測試數據庫層的工作是屬於* integration *或* system *測試的重要步驟。系統測試可以捕獲單元測試無法解決的問題(錯誤的數據庫連接字符串,EF模型與數據庫模式不同步等)。另一方面,你的代碼庫代碼看起來很簡單,許多人([* cough * 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; } 
} 

的問題是EFProductRepository目前預計實現IEFDbContext接口的對象,但是這個接口並沒有定義SaveChanges方法用於我放在星號之間的行,以便編譯器開始抱怨。

定義SaveChanges方法IEFDbContext界面上解決您的問題:

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

    void SaveChanges(); 
} 
+0

我明白了......我確實考慮過這一點,但認爲必須有另一種方式來做到這一點! :)我會給它一個去看看它是怎麼回事......會回到你身邊。謝謝:) – GazB