2010-10-11 64 views
3

我正在嘗試使用內存模擬上下文來測試我的存儲庫。如何模擬實體框架的導航屬性智能?

我像大多數人一樣使用內存中的字典來實現它。這會將我的存儲庫接口上的成員實現爲Add,Remove,Find等,並使用內存中的集合。

這工作正常,在大多數情況下:

[TestMethod] 
public void CanAddPost() 
{ 
    IRepository<Post> repo = new MockRepository<Post>(); 
    repo.Add(new Post { Title = "foo" }); 
    var postJustAdded = repo.Find(t => t.Title == "foo").SingleOrDefault(); 
    Assert.IsNotNull(postJustAdded); // passes 
} 

不過,我有以下的測試,我無法得到與模擬庫通過(對SQL資料庫工作正常)。

考慮我有三個倉庫:

  1. 帖子(處理用戶內容的帖子,像StackOverflow的問題)。
  2. 地點(位置在世界上,如「洛杉磯」)。
  3. LocationPosts(接點表處理郵件/位置之間的許多許多)。

可以將帖子添加到任何地方,也可以將它們添加到特定的位置。

現在,這裏是我的測試:

[TestMethod] 
public void CanAddPostToLocation() 
{ 
    var location = locationRepository.FindSingle(1); // Get LA 
    var post = new Post { Title = "foo", Location = location }; // Create post, with LA as Location. 
    postRepository.Add(post); // Add Post to repository 

    var allPostsForLocation = locationPostRepository.FindAll(1); // Get all LA posts. 
    Assert.IsTrue(allPostsForLocation.Contains(post)); // works for EF, fails for Mock. 
} 

基本上,使用「真正的」 EF/SQL庫的時候,當我一個發佈添加到特定位置,的EntityFramework是足夠聰明地添加了「LocationPost」記錄,因爲EDMX(「郵政」實體上的「LocationPosts」導航屬性)中的關聯

但是,如何使我的Mock存儲庫足夠智能以「模仿」此EF智能?

當我做「添加」我的模擬庫,這只是添加到詞典。它沒有智慧去「哦,等等,你有一個依賴關聯,讓我把它添加到你的其他倉庫」。

我的模擬庫是通用的,所以我不知道如何把智慧放在那裏。

我也看過創建一個FakeObjectContext/FakeObjectSet(如她的博客中的Julie Lerman所建議的),但這仍然不包括這種情況。

我有一種感覺,我的嘲笑解決方案不夠好。任何人都可以提供幫助,或者提供關於如何正確地模擬覆蓋我的場景的Entity Framework 4/SQL Server存儲庫的最新文章?

問題的核心我有每聚合根一個存儲庫(這是好的,但也是我下臺)。

所以郵政位置都是聚集根,但既不是 「擁有」 LocationPosts

因此,他們是3個獨立的存儲庫,而在內存中的情況下,他們是3個單獨的字典。我想我在內存回購中錯過了它們之間的「膠水」。

編輯

問題的部分原因是,我使用純POCO的(無EF代碼生成)。我也沒有任何更改跟蹤(沒有基於快照的跟蹤,沒有代理類)。

我覺得這是「智慧」發生的地方。

目前,我正在探索委託選項。我在「添加」之後在我的通用模擬存儲庫(void,接受泛型T,作爲實體)中公開了一個事件。然後,我在我的「Post Repository」中訂閱此事件,我計劃將相關實體添加到其他存儲庫。

這應該工作。如果是這樣的話,會作爲答案。

但是,我不確定這是最好的解決方案,但是再次,這只是爲了滿足嘲弄(代碼不會用於真正的功能)。

回答

3

正如我在我的編輯中所說,我探討了代表選項,它已成功地工作。

我是這樣做的:

namespace xxxx.Common.Repositories.InMemory // note how this is an 'in-memory' repo 
{ 
    public class GenericRepository<T> : IDisposable, IRepository<T> where T : class 
    { 
     public delegate void UpdateComplexAssociationsHandler<T>(T entity); 
     public event UpdateComplexAssociationsHandler<T> UpdateComplexAssociations; 

     // ... snip heaps of code 

     public void Add(T entity) // method defined in IRepository<T> interface 
     { 
     InMemoryPersistence<T>().Add(entity); // basically a List<T> 
     OnAdd(entity); // fire event 
     } 

     public void OnAdd(T entity) 
     { 
     if (UpdateComplexAssociations != null) // if there are any subscribers... 
      UpdateComplexAssociations(entity); // call the event, passing through T 
     } 
    } 
} 

然後,在我的記憶中「後庫」(從上面的類繼承)。

public class PostRepository : GenericRepository<Post> 
{ 
    public PostRepository(IUnitOfWork uow) : base(uow) 
    { 
     UpdateComplexAssociations += 
        new UpdateComplexAssociationsHandler<Post>(UpdateLocationPostRepository); 
    } 

    public UpdateLocationPostRepository(Post post) 
    { 
     // do some stuff to interrogate the post, then add to LocationPost. 
    } 
} 

你也可以認爲「堅持住,PostRepository 派生從GenericRepository,所以爲什麼要使用委託,你爲什麼不重寫添加?」答案是「Add」方法是IRepository的接口實現 - 因此不能是虛擬的。

正如我所說,不是最好的解決方案 - 但這是一個嘲諷的場景(對於代表來說是個好例子)。我的印象並不是很多人在嘲笑,純粹的POCO和儲存庫/工作模式單元(沒有POCO的變化跟蹤)方面「走得很遠」。

希望這可以幫助別人。

+0

但是'OnAdd'可以是虛擬的,不是嗎? – 2012-03-19 14:48:46