2010-09-01 148 views
7

我試圖製造假上下文accodring到http://blogs.msdn.com/b/adonet/archive/2009/12/17/walkthrough-test-driven-development-with-the-entity-framework-4-0.aspx使用POCO和t4模板測試EF 4.0 - 如何模擬上下文?

,我可以看到有暴露返回IObjectSet < ...>方法的接口,但T4模板生成返回對象集< ...>方法並沒有生成的接口,並在該頁面的作者添加接口創建的上下文,並給他的方式來創建模擬等。

我的主要目標是使用T4模板生成poco類並創建模擬/假上下文來測試我的自定義存儲庫。有沒有什麼辦法可以讓它在沒有寫入或改變T4模板的情況下工作?如何創建上述背景下嘲弄(用於IObjectSet豈不等於簡單),如果它的返回對象集的,而不是IObjectSets ...

THX提前

回答

3

筆者只是嘲諷庫,而不是實體。 EntityFramework生成ObjectQueries,但他包裝它們,他的存儲庫返回IObjectQueries。他這樣做,以便他可以輕鬆地嘲笑數據,然後在保存期間他只驗證實體。

如果您只是想創建一個「模擬」存儲庫,您可以創建自己的T4模板並迭代edmx文件並生成代碼。但是沒有必要產生POCOS的理由?它們已經存在,爲什麼你需要重新創建它們?他將所有東西都抽象成一個「通用」的FakeObjectSet,所以實際上沒有太多的代碼要寫入?

你想產生這樣的:

public IObjectSet<Blog> Blogs 
    { 
     get 
     { 
      return _blogs ?? (_blogs = new FakeObjectSet<Blog>()); 
     } 
     set 
     { 
      _blogs = value as FakeObjectSet<Blog>; 
     } 
    } 
    private FakeObjectSet<Blog> _blogs; 

如果是的話我會想你要花費更多的時間與T4那麼你就只是寫它。

public IObjectSet<Blogs>{ 
    get{ 
    return _Blogs?? (_Blogs = FakeObjectSet<Blog>("Blogs")); 
    } 
    set{ 
    _Blogs= value as FakeObjectSet<Class>("Blogs"); 
    } 
} 

private FakeObjectSet<Blog> _Blogs; 

旁註:


例T4無類的聲明...您可以通過以下this blog

<# 
    foreach (EntitySet set in container.BaseEntitySets.OfType<EntitySet>()) 
    { 
#> 
public IObjectSet<<#=MultiSchemaEscape(set.ElementType, code)#>> 
{ 
    get{ 
     return <#=code.FieldName(set)#> ?? (<#=code.FieldName(set)#> = FakeObjectSet<<#=MultiSchemaEscape(set.ElementType, code)#>>("<#=set.Name#>")); 
    } 
    set{ 
    <#=code.FieldName(set)#> = value as FakeObjectSet<<#=MultiSchemaEscape(set.ElementType, code)#>>("<#=set.Name#>"); 
    } 
} 
private FakeObjectSet<<#=MultiSchemaEscape(set.ElementType, code)#>> <#=code.FieldName(set)#>; 
<# 
} 

#> 

這將生成的代碼做完整的T4 。

IObjectSet被包含在System.Data所以添加引用羅伊Osherove到System.Data.Entity.dll

+0

Mayby我懷念的理解,我只是立足這篇文章。我知道作者有POCOS類,但是我有大的已經存在的數據庫,我必須從這個數據庫創建edmx,然後在很多困難之後(這是Oracle數據庫),我能夠使用T4模板生成POCO類,因爲我沒有親手寫下他們。現在我想添加單元測試,如果要測試存儲庫,我必須模擬我的上下文對象。 因此,T4爲Context創建了實體和類的POCO類,但是這個上下文缺少接口,所以我無法基於此接口創建模擬。 – Simon 2010-09-01 13:25:47

+1

感謝 http://slappyza.wordpress.com/2010/08/08/getting-the-entity-framework-to-generate-an-interface-for-mocking/ 我現在已經解決了問題.. 。:) – Simon 2010-09-01 13:26:33

+0

+1。真的,所有需要發生的事情就是將'I'添加到現有的ObjectSet ...聲明中... – 2010-09-01 13:29:14

1

報價在The Art Of Unit Testing

沒有面向對象的問題,不能通過添加一個間接層來解決,當然除了間接層太多以外。

下面是我可嘲笑的EF4 POCO設置。我沒有使用T4,因爲它很難弄清楚如何清理模板以避免產生太多的垃圾。你當然可以破解T4模板來吐出像這樣的結構。

訣竅是手動創建ObjectSet<T> s並將它們暴露爲IQueryable。由於AddCreate位於ObjectSet<T>/ObjectSet<T>,我還必須添加添加和創建實體的方法。現在

public interface IStackTagzContext { 
    IQueryable<Question> Questions { get; } 

    Question CreateQuestion(); 

    void CreateQuestion(Question question); 

    void SaveChanges(); 
} 

public class StackTagzContext : ObjectContext, IStackTagzContext { 
    public StackTagzContext() : base("name=myEntities", "myEntities") 
    { 
     base.ContextOptions.LazyLoadingEnabled = true; 
     m_Questions = CreateObjectSet<Question>(); 
    } 

    #region IStackTagzContext Members 
    private ObjectSet<Question> m_Questions; 
    public IQueryable<Question> Questions { 
     get { return m_Questions; } 
    } 


    public Question CreateQuestion() { 
     return m_Questions.CreateObject(); 
    } 
    public void AddQuestion(Question question) { 
     m_Questions.AddeObject(question); 
    } 

    public new void SaveChanges() { 
     base.SaveChanges(); 
    } 

    #endregion 
} 

,你會注意到在接口上的實體集合類型IQueryable<T>,而不是IObjectSet<T>。我不能打擾FakeObjectSetIQueryable爲我提供了足夠的靈活性。所以爲了KISS,我沒有它。

嘲笑IQueryable,在另一方面,是微不足道的:

using Moq; 
[TestClass] 
public class TestClass { 

    Mock<IStackTagzContext> m_EntitiesMock = new Mock<IStackTagzContext>(); 


    [TestMethod()] 
    public void GetShouldFilterBySite() { 
     QuestionsRepository target = new QuestionsRepository(m_EntitiesMock.Object); 

     m_EntitiesMock.Setup(e=>e.Questions).Returns(new [] { 
     new Question{Site = "site1", QuestionId = 1, Date = new DateTime(2010, 06,23)}, 
     }.AsQueryable()); 
    } 
}