将测试替身与 DbEntityEntry 和 DbPropertyEntry 一起使用

Posted

技术标签:

【中文标题】将测试替身与 DbEntityEntry 和 DbPropertyEntry 一起使用【英文标题】:Using Test Doubles with DbEntityEntry and 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?谢谢!

【问题讨论】:

【参考方案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;
        
    

【讨论】:

以上是关于将测试替身与 DbEntityEntry 和 DbPropertyEntry 一起使用的主要内容,如果未能解决你的问题,请参考以下文章

Node.js Sinon测试替身

Go项目的测试代码3(测试替身Test Double)

将 SaveChange 中的旧值和新值保存为 DbEntityEntry.Entity 以进行审计

测试替身的类型

在不同的测试目标之间有效地共享测试替身

DBEntityEntry类