[英]How Do I Mock Entity Framework's Navigational Property Intelligence?
我正在嘗試使用內存中的模擬上下文來測試我的存儲庫。
正如大多數人所做的那樣,我使用內存中的字典來實現它。 這將我的存儲庫接口上的成員實現為使用內存中集合的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存儲庫)。
考慮我有三個存儲庫:
帖子可以添加到任何地方,也可以添加特定的位置。
現在,這是我的測試:
[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智能?
當我在我的模擬存儲庫上執行“添加”時,這只會添加到字典中。 它沒有聰明才能去“哦,等等,你有一個依賴關聯,讓我把它添加到OTHER存儲庫”。
我的模擬存儲庫是通用的,所以我不知道如何把智能放在那里。
我還看過創建一個FakeObjectContext / FakeObjectSet(正如Julie Lerman在她的博客上所建議的那樣),但是這仍然沒有涵蓋這個場景。
我有一種感覺,我的嘲弄解決方案還不夠好。 任何人都可以提供幫助,或者提供有關如何正確模擬覆蓋我的場景的Entity Framework 4 / SQL Server存儲庫的最新文章嗎?
問題的核心是我每個聚合根有一個存儲庫(這很好,但也是我的垮台)。
所以Post和Location都是聚合根,但都不是“擁有” LocationPosts 。
因此,它們是3個單獨的存儲庫,在內存中,它們是3個單獨的字典。 我想我在內存回購中錯過了它們之間的“粘合劑”。
編輯
問題的一部分是我使用Pure POCO (沒有EF代碼生成)。 我也沒有任何更改跟蹤(沒有基於快照的跟蹤,沒有代理類)。
我認為這是“聰明人”發生的地方。
目前,我正在探索代表選項。 我在我的Generic Mock Repository中暴露了一個事件(void,接受泛型T,作為Entity),我在“Add”之后調用它。 然后,我在“Post Repository”中訂閱此事件,我計划將相關實體添加到其他存儲庫。
這應該工作。 如果它這樣做,將作為答案。
但是,我不確定這是最好的解決方案,但是再一次,這只是為了滿足模擬(代碼不會用於實際功能)。
正如我在編輯中所說,我探索了委托選項,該選項已成功運作。
這是我如何做到的:
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
}
}
}
然后,在我的In Memory“Post Repository”(繼承自上面的類)。
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的變化跟蹤)方面“走得這么遠”。
希望這有助於其他人。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.