将测试双打与DbEntityEntry和DbPropertyEntry一起使用

时间:2014-11-12 19:13:59

标签: testing moq entity-framework-6 dbcontext

我正在使用EF6中的新测试双打here from MSDN。 VS2013与Moq& NUnit的。 一切都很好,直到我不得不做这样的事情:

  

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

然后:

  

myFoo.Name = "Bar";

然后:

  

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

此时我收到错误:

  

其他信息:会员' IsModified'无法召唤   财产'姓名'因为类型的实体   '富'在上下文中不存在。添加一个   实体向上下文调用Add或Attach方法   DbSet。

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

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

  

Foos = new FakeDbSet<Foo>();

我的问题是,是否可以使用测试双打场景使用FakeDbSet和FakeContext,以便从测试双重访问DbEntityEntry和DBPropertyEntry?谢谢!

2 个答案:

答案 0 :(得分:5)

  

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

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

坦率地说,我并不了解所有这些关于嘲笑EF的博客和文章。只有众多的differences between LINQ to objects and LINQ to entites应该足以阻止它。这些模拟上下文和DbSet构建了一个全新的宇宙,它本身就是一个bug的来源。我决定只在我的代码中涉及EF的时间和地点进行集成测试。一个有效的端到端测试让我感觉事情还可以。单元测试(伪造EF)不是。 (别人这样做,不要误解我的意思)。

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

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

所以,回答你的问题

  

是否可以(...)从测试双重访问DbEntityEntry和DBPropertyEntry?

不,EF的嘲笑钩子只是非常肤浅的,你甚至都不会接近EF真正的作用。

答案 1 :(得分:0)

抽象它。如果您正在使用某个界面,那么在创建自己的双打时,请将修改过的内容放在一个单独的方法中。我的界面和实现(由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;
        }
    }
}