利用Mocking Framework 单元测试Entity Framework

一、前言

  在实际编写程序时,往往需要与数据库打交道,在单元测试中直接使用数据库又显得太重,如果可以方便的编写一些测试数据,这样更易于检测功能。如何模拟数据库行为便是本篇的主题。微软有教程说明Moq Entity Framework,需注意的是EF的版本必须是6以上。但在这篇教程中是直接使用DbContext,而自己的应用程序中都是用UnitOfWork模式。经过修改后也可以实现类似功能。

二、参考文献

https://msdn.microsoft.com/en-us/data/dn314429

三、采用UnitOfWork模式管理数据库

UnitOfWork

 public interface IDomainUnitOfWork : IDisposable
    {
        DbContext Db { get; }
        //ImsDbContext dbContext { get; }
        Task SaveChangesClientWinAsync();
        Task SaveChangesDataBaseWinAsync();

        void SaveChangesClientWin();
        void SaveChangesDataBaseWin();
    }

Repository接口

 public interface IDomainRepositoryAsync<T> where T : class
    {

        //Async

        Task<List<T>> GetAllAsync(Expression<Func<T, bool>> predicate = null);

        Task<T> SingleAsync(object primaryKey);
        Task<T> SingleOrDefaultAsync(object primaryKey);
        Task<bool> IsExistsAsync(Expression<Func<T, bool>> predicate = null);

        //同步
        IQueryable<T> GetAll(Expression<Func<T, bool>> predicate = null);
        T Single(object primaryKey);
        T SingleOrDefault(object primaryKey);
        bool IsExist(Expression<Func<T, bool>> predicate = null);
        void Add(T entity);
        void Update(T entity);
        void Delete(T entity);

    }

Repository实现

public class DomainRepositoryAsync<T> : IDomainRepositoryAsync<T> where T : class
    {
        private readonly IDomainUnitOfWork _unitOfWork;
        internal DbSet<T> dbSet;

        public DomainRepositoryAsync(IDomainUnitOfWork unitOfWork)
        {
            if (unitOfWork == null) throw new ArgumentNullException("unitOfWork");
            this._unitOfWork = unitOfWork;
            this.dbSet = _unitOfWork.Db.Set<T>();
        }
        public void Add(T entity)
        {
            dynamic obj = dbSet.Add(entity);
        }

        public void Delete(T entity)
        {
            if (_unitOfWork.Db.Entry(entity).State == EntityState.Detached)
            {
                dbSet.Attach(entity);
            }
            dynamic obj = dbSet.Remove(entity);
        }

        public async Task<List<T>> GetAllAsync(Expression<Func<T, bool>> predicate = null)
        {

            if (predicate != null)
            {
                return await this.dbSet.Where(predicate).ToListAsync();
            }

            return await this.dbSet.ToListAsync();
        }

        public async Task<bool> IsExistsAsync(Expression<Func<T, bool>> predicate = null)
        {
            var result = false;
            if (predicate == null)
            {
                return result;
            }
            var query = await this.dbSet.Where(predicate).FirstOrDefaultAsync();
            result = query == null ? false : true;
            return result;
        }
        /// <summary>
        /// 如果没有找到指定键元素,抛出异常.
        /// </summary>
        /// <param name="primaryKey">The primary key.</param>
        /// <returns>Task&lt;T&gt;.</returns>
        /// <exception cref="System.Collections.Generic.KeyNotFoundException"></exception>
        public async Task<T> SingleAsync(object primaryKey)
        {
            T dbResult = null;
            dbResult = await dbSet.FindAsync(primaryKey);
            if (dbResult == null)
            {
                throw new KeyNotFoundException();
            }
            return dbResult;
        }

        public async Task<T> SingleOrDefaultAsync(object primaryKey)
        {
            var dbResult = await dbSet.FindAsync(primaryKey);
            return dbResult;
        }

        public void Update(T entity)
        {
            this.dbSet.Attach(entity);
            _unitOfWork.Db.Entry(entity).State = EntityState.Modified;
        }

        public IQueryable<T> GetAll(Expression<Func<T, bool>> predicate = null)
        {
            if (predicate != null)
            {
                return this.dbSet.Where(predicate);
            }

            return this.dbSet;
        }

        public T Single(object primaryKey)
        {
            T dbResult = null;
            dbResult = dbSet.Find(primaryKey);
            if (dbResult == null)
            {
                throw new KeyNotFoundException();
            }
            return dbResult;
        }

        public T SingleOrDefault(object primaryKey)
        {
            var dbResult = dbSet.Find(primaryKey);
            return dbResult;
        }

        public bool IsExist(Expression<Func<T, bool>> predicate = null)
        {
            var result = false;
            if (predicate == null)
            {
                return result;
            }
            var query = this.dbSet.Where(predicate).FirstOrDefault();
            result = query == null ? false : true;
            return result;
        }
    }

DbContext

public class DurationDbContext : DbContext
    {
        public virtual DbSet<Department> Departments { get; set; }
        public virtual DbSet<Duration> Durations { get; set; }
    }            

四、配置UnitTest

1、首先用Nuget安装moq

2、注意在DomainRepositoryAsync中有一个DbSet<T> dbSet,需要Moq的就是该类型,并且在DbContext中的必须加"virtual“关键字。

3、Moq代码

var unitOfWork = new Mock<IDomainUnitOfWork>();
 var mockDepartment = SetupMockDbSet<Department>(DepartmentList);
 var mockDuration = SetupMockDbSet<Duration>(DurationList);
 unitOfWork.Setup(m => m.Db.Set<Department>()).Returns(mockDepartment.Object);
 unitOfWork.Setup(m => m.Db.Set<Duration>()).Returns(mockDuration.Object);

在教程中说到了如何处理异步的查询操作,教程很详细,此处便不再重复,直接将代码Copy到单元测试工程中即可,再将重复的代码作为一个方法SetupMockDBSet。

 public static Mock<DbSet<T>> SetupMockDbSet<T>(List<T> dataList) where T : class
        {
            var data = dataList.AsQueryable();
            var mockSet = new Mock<DbSet<T>>();
            mockSet.As<IDbAsyncEnumerable<T>>()
               .Setup(m => m.GetAsyncEnumerator())
               .Returns(new TestDbAsyncEnumerator<T>(data.GetEnumerator()));

            mockSet.As<IQueryable<T>>()
                .Setup(m => m.Provider)
                .Returns(new TestDbAsyncQueryProvider<T>(data.Provider));

            mockSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(data.Expression);
            mockSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(data.ElementType);
            mockSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
            return mockSet;
        }

注意在配置UnitOfWork时,用Setup中是对IUnitOfWork的Db进行设置。接下来的实现方式与教程相同,不再重复。

时间: 2024-12-22 21:58:54

利用Mocking Framework 单元测试Entity Framework的相关文章

Entity Framework 教程——Entity Framework中的实体类型

Entity Framework中的实体类型 : 在之前的章节中我们介绍过从已有的数据库中创建EDM,它包含数据库中每个表所对应的实体.在EF 5.0/6.0中,存在POCO 实体和动态代理实体两种. POCO Entity (Plain Old CLR Object): POCO类是不依赖任何框架的类型,如同其他正常的一般类型,我们称之为"Plain Old CLR Objects"(这里不知道怎么翻译,普通的CLR对象?古老的CLR对象?大概意思就是没有什么特殊的对象吧). POC

深入了解Entity Framework框架及访问数据的几种方式

一.前言 1.Entity Framework概要 Entity Framework是微软以ADO.NET为基础所发展出来的对象关系映射(O/R Mapping)解决方案.该框架曾经为.NET Framework的一部分,但Version 6之后从.NET Framework分离出来,可通过NuGet获取. Entity Framework利用抽象化数据结构的方式,将每个数据库对象都转换成应用程序对象 (Entity),而数据字段都转换为属性 (Property),关系则转换为结合属性 (Ass

Entity Framework中IQueryable, IEnumerable, IList的区别

小分享:我有几张阿里云优惠券,用券购买或者升级阿里云相应产品最多可以优惠五折!领券地址:https://promotion.aliyun.com/ntms/act/ambassador/sharetouser.html?userCode=ohmepe03 使用工具追踪EF生成的SQL 使用Entity Framework等ORM框架的时候,SQL对于使用者来说是透明的,往往很多人也不关心ORM所生成的SQL,然而系统出现性能问题的时候就必须关注生成的SQL以发现问题所在. 使用过Toplink的

ASP.NET MVC 5 + EF 6 入门教程 (5) Model和Entity Framework

文章来源: Slark.NET-博客园 http://www.cnblogs.com/slark/p/mvc-5-ef-6-get-started-model.html 上一节:ASP.NET MVC 5 入门教程 (4) View和ViewBag 源码下载:点我下载 MVC中的Model是用来给View提供显示数据的对象. 这里我们首先创建一个Model对象. 在解决方案资源管理器中右键点击Models文件夹,选择添加->类.添加一个名为Employee.cs的Model类.Models文件夹

Entity Framework 全面教程详解(转)

目录 预备知识    2 LINQ技术 2 LINQ技术的基础 - C#3.0    2 自动属性    2 隐式类型    2 对象初始化器与集合初始化器    3 匿名类    3 扩展方法    4 Lambda表达式    4 .NET中的数据访问    4 DataSet方案    5 改进的的DataSet方案    5 手写代码通过ADO.NET2.0连接类与数据库交互    5 ORM – LINQ to SQL    6 深入了解Entity Framework    7 En

使用工具追踪Entity Framework生成的SQL

学习entity framework期间收集的文章,转自http://www.cnblogs.com/hiteddy/archive/2011/10/01/Difference_among_IQueryable_IEnumeralb_IList_in_Entity_Framework.html 使用Entity Framework等ORM框架的时候,SQL对于使用者来说是透明的,往往很多人也不关心ORM所生成的SQL,然而系统出现性能问题的时候就必须关注生成的SQL以发现问题所在. 使用过Top

ASP.NET MVC5 + EF6 入门教程 (5) Model和Entity Framework

文章来源: Slark.NET-博客园 http://www.cnblogs.com/slark/p/mvc-5-ef-6-get-started-model.html 上一节:ASP.NET MVC 5 入门教程 (4) View和ViewBag 源码下载:点我下载 一.创建Model MVC中的Model是用来给View提供显示数据的对象. 这里我们首先创建一个Model对象. 在解决方案资源管理器中右键点击Models文件夹,选择添加->类.添加一个名为Employee.cs的Model类

Entity Framework Tutorial Basics(2):What is Entity Framework?

What is Entity Framework? Writing and managing ADO.Net code for data access is a tedious and monotonous job. Microsoft has provided an O/RM framework called "Entity Framework" to automate database related activities for your application. Microso

Entity Framework Tutorial Basics(4):Setup Entity Framework Environment

Setup Entity Framework Environment: Entity Framework 5.0 API was distributed in two places, in NuGet package and in .NET framework. The .NET framework 4.0/4.5 included EF core API, whereas EntityFramework.dll via NuGet package included EF 5.0 specifi