使用POCO和t4模板测试EF 4.0 - 模拟上下文如何?

时间:2010-09-01 12:01:37

标签: c# entity-framework-4 t4

我正在尝试创建符合http://blogs.msdn.com/b/adonet/archive/2009/12/17/walkthrough-test-driven-development-with-the-entity-framework-4-0.aspx

的假上下文

正如我所看到的,有一个接口公开了返回IObjectSet< ...>的方法,但是T4模板生成了返回ObjectSet< ...>的方法。并且没有生成的界面,并且在该页面上作者为创建的上下文添加了界面,并且它为他提供了创建模拟等的方法。

我的主要目标是使用T4模板生成poco类并创建模拟/伪上下文来测试我的自定义存储库。有没有办法让它无需编写或更改T4模板?如果它返回ObjectSet而不是IObjectSets,我如何在上下文之上创建模拟(对于IObjectSet并不是一件容易的事)...

提前谢谢

2 个答案:

答案 0 :(得分: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上,那么你就会写它。


没有类声明的示例T4 ...您可以按照this blog

执行完整的t4
<#
    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)#>;
<#
 }

#>

哪会生成此代码:

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

private FakeObjectSet<Blog> _Blogs;

旁注。

IObjectSet包含在System.Data中,因此添加对System.Data.Entity.dll的引用

答案 1 :(得分:1)

Roy Osherove在The Art Of Unit Testing引用:

  

没有面向对象的问题,通过添加一个间接层无法解决,当然,除了间接层太多外。

以下是我的可模拟EF4 POCO设置。我没有使用T4,因为很难弄清楚如何清理模板以不产生太多的gumpf。你当然可以破解T4模板来吐出类似这种结构的东西。

诀窍是手动创建ObjectSet<T>并将其公开为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());
  }
}