【问题标题】:Testing EF 4.0 with POCO and t4 templates - How mock context?使用 POCO 和 t4 模板测试 EF 4.0 - 如何模拟上下文?
【发布时间】:2010-09-01 12:01:36
【问题描述】:

我正在尝试根据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 并不简单)...

提前谢谢

【问题讨论】:

    标签: c# entity-framework-4 t4


    【解决方案1】:

    作者只是在模拟存储库,而不是实体。 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 的引用

    【讨论】:

    • 也许我被错过了,我只是基于这篇文章。我知道作者有 POCOS 类,但我有大的、已经存在的数据库,我必须从这个数据库创建 edmx,接下来经过许多困难(这是 Oracle 数据库),我能够使用 T4 模板生成 POCO 类,因为我以前没有手写它们。现在我想添加单元测试,如果我想测试存储库,我必须模拟我的上下文对象。所以 T4 为实体生成 POCO 类,为 Context 生成类,但是缺少此上下文的接口,因此我无法基于此接口创建模拟。
    • 感谢slappyza.wordpress.com/2010/08/08/… 我现在已经解决了问题... :)
    • +1。真的,需要做的就是将I 添加到现有的ObjectSet... 声明中......
    【解决方案2】:

    The Art Of Unit Testing Roy Osherove 中引用:

    没有什么面向对象的问题不能通过增加一层间接层来解决,当然,除了太多的间接层。

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

    诀窍是手动创建ObjectSet&lt;T&gt;s 并将它们公开为IQueryable。因为AddCreateObjectSet&lt;T&gt;/ObjectSet&lt;T&gt; 上,所以我还必须添加用于添加和创建实体的方法。

    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&lt;T&gt;,而不是IObjectSet&lt;T&gt;。创建FakeObjectSetIQueryable 为我提供了足够的灵活性。所以为了亲吻,我没有它。

    另一方面,嘲笑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());
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-10-16
      • 1970-01-01
      • 1970-01-01
      • 2011-05-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多