2010-09-01 149 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()); 
    } 
}