【问题标题】:Using Test Doubles with DbEntityEntry and DbPropertyEntry将测试替身与 DbEntityEntry 和 DbPropertyEntry 一起使用
【发布时间】:2015-01-09 18:19:11
【问题描述】:

我正在使用here from MSDN 概述的 EF6 中的新测试替身。带有最小起订量和 nUnit 的 VS2013。 一切都很好,直到我不得不做这样的事情:

var myFoo = context.Foos.Find(id);

然后:

myFoo.Name = "Bar";

然后:

context.Entry(myFoo).Property("Name").IsModified = true;

此时出现错误:

附加信息:无法调用成员“IsModified” 属性“名称”,因为类型的实体 上下文中不存在“Foo”。添加一个 实体到上下文调用 Add 或 Attach 方法 数据库集。

虽然,当我使用 AddWatch 检查上下文中的“Foos”时,我可以在运行测试之前看到我添加的所有项目。所以他们在那里。

我从文章中创建了 FakeDbSet(或 TestDbSet)。我将每个 FakeDbSet 放在 FakeContext 中的每个都被初始化的构造函数中。像这样:

Foos = new FakeDbSet<Foo>();

我的问题是,是否可以在测试替身场景中使用 FakeDbSet 和 FakeContext,以便从测试替身访问 DbEntityEntry 和 DBPropertyEntry?谢谢!

【问题讨论】:

    标签: testing moq entity-framework-6 dbcontext


    【解决方案1】:

    我可以在运行测试之前查看我添加的所有项目。所以他们就在那里。

    实际上,您只向ObservableCollection 添加了项目。 context.Entry 方法比这更深入。它需要变更跟踪器积极参与添加、修改和删除实体。如果你想模拟这个变化跟踪器ObjectStateManager(忽略它根本不是为了模拟而设计的),祝你好运!它有超过 4000 行代码。

    坦率地说,我不了解所有这些关于模拟 EF 的博客和文章。只有众多的differences between LINQ to objects and LINQ to entites 足以阻止它。这些模拟上下文和DbSets 构建了一个全新的宇宙,它本身就是错误的来源。我决定只在我的代码中涉及 ​​EF 的时间和地点进行集成测试。一个有效的端到端测试给了我一种很好的感觉,那就是一切都很好。单元测试(伪造 EF)没有。 (其他人会,不要误会我的意思)。

    但是让我们假设你仍然想冒险嘲笑DbContext.Entry<T>。太糟糕了,不可能。

    • 方法不是虚拟的
    • 它返回一个DbEntityEntry<T>,一个具有内部构造函数的类,它是一个InternalEntityEntry 的包装器,它是一个内部类。而且,顺便说一句,DbEntityEntry 没有实现接口。

    所以,回答你的问题

    是否可以(...)从测试替身中访问 DbEntityEntry 和 DBPropertyEntry?

    不,EF 的模拟钩子只是非常肤浅,你甚至永远无法接近 EF 的真正工作原理。

    【讨论】:

    • 有时需要进行单元测试,而不是集成使用 EF 的代码之一。例如我想测试我的代码如何处理 DbUpdateException。对我来说,这是一个经典的单元测试,需要只模拟异常,而不关心实际的 EF 行为。而且因为模拟 EF 类是不可能的,我不得不将我的单元测试移到集成测试项目中
    【解决方案2】:

    只是抽象它。如果您正在使用接口,则在创建自己的双打时,将修改后的内容放在单独的方法中。我的接口和实现(由 EF 生成,但我更改了模板)如下所示:

    //------------------------------------------------------------------------------
    // <auto-generated>
    //     This code was generated from a template.
    //
    //     Manual changes to this file may cause unexpected behavior in your application.
    //     Manual changes to this file will be overwritten if the code is regenerated.
    // </auto-generated>
    //------------------------------------------------------------------------------
    
    namespace Model
    {
        using System;
        using System.Data.Entity;
        using System.Data.Entity.Infrastructure;
    
        public interface IOmt
        {
            DbSet<DatabaseOmtObjectWhatever> DatabaseOmtObjectWhatever { get; set; }
            int SaveChanges();
            void SetModified(object entity);
            void SetAdded(object entity);
        }
    
        public partial class Omt : DbContext, IOmt
        {
            public Omt()
                : base("name=Omt")
            {
            }
    
            protected override void OnModelCreating(DbModelBuilder modelBuilder)
            {
                throw new UnintentionalCodeFirstException();
            }
    
            public virtual DbSet<DatabaseOmtObjectWhatever> DatabaseOmtObjectWhatever { get; set; }
    
            public void SetModified(object entity)
            {
                Entry(entity).State = EntityState.Modified;
            }
            public void SetAdded(object entity)
            {
                Entry(entity).State = EntityState.Added;
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-08-18
      • 2020-04-04
      • 2011-01-14
      • 1970-01-01
      • 2019-10-26
      • 2020-09-04
      • 2016-02-18
      相关资源
      最近更新 更多