单元测试 mock EF 中DbContext 和DbSet

现在EF越来越流行了,很多时候业务成都是直接访问DbContext 和DbSet来操作数据的。 那么我们测试的时候如何来mock这2个对象了?现在时间很晚了, 就直接贴code吧:首先建一个帮助类:

 public class MockedDbContext<T> : Mock<T> where T : DbContext
    {
        public Dictionary<string, object> Tables
        {
            get { return _Tables ?? (_Tables = new Dictionary<string, object>()); }
        }
        private Dictionary<string, object> _Tables;

    }
    public static class EntityFrameworkMockHelper
    {
        /// <summary>
        /// Returns a mock of a DbContext
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static MockedDbContext<T> GetMockContext<T>() where T : DbContext
        {
            var instance = new MockedDbContext<T>();
            instance.MockTables();
            return instance;
        }

        /// <summary>
        /// Use this method to mock a table, which is a DbSet{T} oject, in Entity Framework.
        /// Leave the second list null if no adds or deletes are used.
        /// </summary>
        /// <typeparam name="T">The table data type</typeparam>
        /// <param name="table">A List{T} that is being use to replace a database table.</param>
        /// <returns></returns>
        public static DbSet<T> MockDbSet<T>(List<T> table) where T : class
        {
            var dbSet = new Mock<DbSet<T>>();
            dbSet.As<IQueryable<T>>().Setup(q => q.Provider).Returns(() => table.AsQueryable().Provider);
            dbSet.As<IQueryable<T>>().Setup(q => q.Expression).Returns(() => table.AsQueryable().Expression);
            dbSet.As<IQueryable<T>>().Setup(q => q.ElementType).Returns(() => table.AsQueryable().ElementType);
            dbSet.As<IQueryable<T>>().Setup(q => q.GetEnumerator()).Returns(() => table.AsQueryable().GetEnumerator());
            dbSet.Setup(set => set.Add(It.IsAny<T>())).Callback<T>(table.Add);
            dbSet.Setup(set => set.AddRange(It.IsAny<IEnumerable<T>>())).Callback<IEnumerable<T>>(table.AddRange);
            dbSet.Setup(set => set.Remove(It.IsAny<T>())).Callback<T>(t => table.Remove(t));
            dbSet.Setup(set => set.RemoveRange(It.IsAny<IEnumerable<T>>())).Callback<IEnumerable<T>>(ts =>
            {
                foreach (var t in ts) { table.Remove(t); }
            });
            return dbSet.Object;
        }

        /// <summary>
        /// Mocks all the DbSet{T} properties that represent tables in a DbContext.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="mockedContext"></param>
        public static void MockTables<T>(this MockedDbContext<T> mockedContext) where T : DbContext
        {
            Type contextType = typeof(T);
            var dbSetProperties = contextType.GetProperties().Where(prop => (prop.PropertyType.IsGenericType) && prop.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>));
            foreach (var prop in dbSetProperties)
            {
                var dbSetGenericType = prop.PropertyType.GetGenericArguments()[0];
                Type listType = typeof(List<>).MakeGenericType(dbSetGenericType);
                var listForFakeTable = Activator.CreateInstance(listType);
                var parameter = Expression.Parameter(contextType);
                var body = Expression.PropertyOrField(parameter, prop.Name);
                var lambdaExpression = Expression.Lambda<Func<T, object>>(body, parameter);
                var method = typeof(EntityFrameworkMockHelper).GetMethod("MockDbSet").MakeGenericMethod(dbSetGenericType);
                mockedContext.Setup(lambdaExpression).Returns(method.Invoke(null, new[] { listForFakeTable }));
                mockedContext.Tables.Add(prop.Name, listForFakeTable);
            }
        }

    }

再来看看的我们DbContext的类吧:

 public class BloggerEntities : DbContext
    {
        public BloggerEntities()
            : base("BloggerEntities")
        {
            Configuration.ProxyCreationEnabled = false;
        }

        public virtual DbSet<Blog> Blogs { get; set; }
        public virtual DbSet<Article> Articles { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Configurations.Add(new ArticleConfiguration());
            modelBuilder.Configurations.Add(new BlogConfiguration());
        }
    }

public virtual DbSet<Blog> Blogs { get; set; }

public virtual DbSet<Article> Articles { get; set; }

注意了,我一般DbSet属性是没有添加virtual, 结果上面的 mockedContext.Setup(lambdaExpression).Returns(method.Invoke(null, new[] { listForFakeTable }));这句一直报错,搞了我2个小时都没有搞定。不能mock 实例方法。

单元测试code:

  static void Main(string[] args)
        {
            var context = EntityFrameworkMockHelper.GetMockContext<BloggerEntities>().Object;
            context.Articles.Add(new Article
            {
                Author = "Gavin",
                BlogID = 1,
                Contents = "test",
                ID = 2,
                Title = "test title",
                URL = "article URL"
            });
            List<Blog> blogs = new List<Blog> {
            new Blog
            {
                ID = 1,
                URL = "blog url",
                Name = "blogs name"
            },
            new Blog
            {
                ID = 1,
                URL = "blog url",
                Name = "blogs name2222"
            }
             };

            //add
            context.Blogs.AddRange(blogs);
            //query
            var query1 = (from a in context.Articles
                          join b in context.Blogs on a.BlogID equals b.ID
                          select new { Author = a.Author, BlogName = b.Name }).ToList();
            //remove
            var blog = context.Blogs.FirstOrDefault(x => x.Name == "blogs name2222");
            context.Blogs.Remove(blog);
            //update
            context.Articles.FirstOrDefault(x=>x.ID==2).URL = "updated url";
            var query2 = (from a in context.Articles
                          join b in context.Blogs on a.BlogID equals b.ID
                          select new { Author = a.Author, ArticleUrl = a.URL }).ToList();

        }

参考:

Testing
with a mocking framework (EF6 onwards)

How
to mock an Entity Framework DbContext and its DbSet properties

文件下载地址:http://download.csdn.net/detail/dz45693/9514948

时间: 2024-10-14 11:25:31

单元测试 mock EF 中DbContext 和DbSet的相关文章

EF中执行sql语句

EF原理 EF 会自动把 Where().OrderBy().Select()等这些编译成"表达式树(Expression Tree)",然后会把表达式树翻译成 SQL 语句去执行.(编译原理,AST)因此不是"把数据都取到内存中,然后使用集合的方法进行数据过滤",因此性能不会低.但是如果这个操作不能被翻译成 SQL 语句,则或者报错,或者被放到内存中操作,性能就会非常低 跟踪EF的查询Sql语句: DbContext 有一个 Database 属性,其中的 Log

DB表的关系及EF中Fluent API的使用

现在使用多数的数据库是关系型数据库,那么表与表之间的关系就会显得尤其重要,对于数据的CRUD处理和以后数据的分析有很大的好处.下面是对于数据库中对表关系的理解以及在EF中使用Fluent API来创建这种关系的例子. 数据库中实体之间的联系 书中语录:在现实世界中,事务内部以及事务之间是有联系的,这些联系在计算机里面叫做实体之间的联系和实体内部的联系,内部的联系就是组成实体属性之间的联系,而实体之间的联系可以分为三类.(1:1, 1:n, n:n) 一:一对一联系(1:1) A实体集中每一个实体

EF 中 Code First 的数据迁移以及创建视图

写在前面: EF 中 Code First 的数据迁移网上有很多资料,我这份并没什么特别.Code First 创建视图网上也有很多资料,但好像很麻烦,而且亲测好像是无效的方法(可能是我太笨,没搞成功),我摸索出了一种简单有效的方法,这里分享给大家. EF是Entity Framework(实体框架)的简写,是微软出品的用来操作数据库的一个框架,会ASP.NET MVC的朋友对他肯定都不陌生.由于学艺不精,我对EF存在一疑虑,就不以[提问]的方式来问了,我以[总结]的方式来表达,如果总结有误的地

EF中的Code First

EF中的Code First   一些概念 ? POCO POCO(Plain Old CLR Object)的概念是从java的POJO借用而来,而两者的含义是一致的,不同的仅仅是使用的语言不一样.所以POCO的解释就是“Plain Old C# Object”.POJO的内在含义是指那些没有从任何类继承.也没有实现任何接口,更没有被其它框架侵入的对象. ? PO PO是指持久对象(persistant object持久对象).持久对象实际上必须对应数据库中的entity,所以和POJO有所区

(转)EF中数据优先,模型优先和代码优先

看了很多文章也不是很明白,说的都太书面化了,大家看看这图一下子就明白了,哈哈 其实看图很简单,database first和model first都是通过 data model创建的edmx文件,只不过model first模块可以自己根据需要创建和修改实体,显得更加灵活. codefist是一个class代码文件,它可以由一些第三方的软件可视化的创建,也是非常灵活的一种方式,目前被使用也是最广泛的. 二.Entity Framework Database first(数据库优先)使用过程 2.

EntityFramework_MVC4中EF5 新手入门教程之四 ---4.在EF中创建更复杂的数据模型

在以前的教程你曾与一个简单的数据模型,由三个实体组成.在本教程中,您将添加更多的实体和关系,并通过指定格式. 验证和数据库映射规则,您将自定义数据模型.你会看到自定义的数据模型的两种方式: 通过添加属性,实体类并通过将代码添加到数据库上下文类. 当您完成时,实体类将已完成的数据模型中,如下图所示: 通过使用属性进行自定义的数据模型 在本节中,您会看到如何通过使用指定的格式,验证和数据库映射规则的属性来自定义数据模型.然后在以下各节,您将创建的几个完整的School数据模型,通过添加属性的类已创建

1.Relationship in Entity Framework Using Code First Approach With Fluent API【使用EF Code-First方式和Fluent API来探讨EF中的关系】

In this article, you will learn about relationships in Entity Framework using the Code First Approach with Fluent API. 在这篇文章中,你将会学习到使用EF Code-First方式和Fluent API来探讨EF中的关系(一对一,一对多,多对多). Introduction[介绍] A relationship, in the context of databases, is a

MVC、EF中,创建更加复杂的数据模型

在前面的课程中,你已经创建了一个简单的由三个实体组成的数据模型.在这个课程中,你将要增加更多的实体,以及关系,使用数据标注特性来控制模型类的行为. 在完成的时候,实体类表示的完整数据模型如下所示: 4-1 使用特性控制格式.验证以及数据库映射 在这一节中,你将会看到如何使用特性来控制数据模型的格式化.验证以及数据库映射.然后在后继的节中,将要通过为已经创建的类.新创建的类增加特性,来创建完整的 School 数据模型. 4-1-1 DisplayFormat 特性 对于学生的注册日期来说,虽然你

EF中三大开发模式之DB First,Model First,Code First以及在Production Environment中的抉择

一:ef中的三种开发方式 1. db first... db放在第一位,在我们开发之前必须要有完整的database,实际开发中用到最多的... <1> DBset集合的单复数... db => model 2. model first... 根据model生成数据库,和我们的sqlserver的可视化类视图是一样的... 其实sqlserver也是有自己的类视图... 如果用ef来创建: <1> 生成类图.. <2> 根据模型生成数据库... 3. code f