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

首先看看的我们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 = ,
Contents = "test",
ID = ,
Title = "test title",
URL = "article URL"
});
List<Blog> blogs = new List<Blog> {
new Blog
{
ID = ,
URL = "blog url",
Name = "blogs name"
},
new Blog
{
ID = ,
URL = "blog url",
Name = "blogs name2222"
}
};
context.Articles.First().Blog = blogs[]; //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==).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(); EFService service = new EFService(context);
var includetest1 = service.GetArticles();
var includetest2 = service.GetArticles2();
var includetest3 = service.GetArticles3();
}

注意为了 测试include 这里我单独写了一个 service

  public class EFService
{
BloggerEntities DBContext { set; get; }
public EFService(BloggerEntities ctx)
{
DBContext = ctx;
}
public List<Article> GetArticles()
{
return DBContext.Articles.Include("Blog").ToList();
}
public List<Article> GetArticles2()
{
return DBContext.Articles.Include(x=>x.Blog).ToList();
}
public List<Article> GetArticles3()
{
return (from a in DBContext.Articles select a).Include(x => x.Blog).ToList();
}
}

我这里的EFService 和BloggerEntities在同一个 程序集里面, 实际上我们应该分开的。

EntityFrameworkMockHelper 的实现如下:

  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); }
});
dbSet.Setup(set => set.Include(It.IsAny<string>())).Returns(dbSet.Object);
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()[];
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);
}
} }

参考:

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

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

  1. EF中DbContext的生命周期

    /// <summary>/// 依赖注入系统中类的生命周期./// </summary>public enum DependencyLifeStyle{    /// < ...

  2. EF中的上下文(DbContext)简介

    DbContext是实体类和数据库之间的桥梁,DbContext主要负责与数据交互,主要作用: 1.DbContext包含所有的实体映射到数据库表的实体集(DbSet < TEntity > ...

  3. EF Core中DbContext可以被Dispose多次

    我们知道,在EF Core中DbContext用完后要记得调用Dispose方法释放资源.但是其实DbContext可以多次调用Dispose方法,虽然只有第一次Dispose会起作用,但是DbCon ...

  4. 教育单元测试mock框架优化之路(中)

    转载:https://sq.163yun.com/blog/article/169564470918451200 三.间接依赖的bean的mock替换 对于前面提供的@Mock,@Spy+@Injec ...

  5. 【IDEA】单元测试:项目中引入JUnit测试框架+Mock简单了解

    一.Junit 使用和说明: 参考:单元测试第三弹--使用JUnit进行单元测试-HollisChuang's Blog http://www.hollischuang.com/archives/17 ...

  6. 在 ef 中执行 DbContext.Table.AddRange(Enitites).ToList() 会发生什么

    在 ef 中执行 DbContext.Table.AddRange(Enitites).ToList() 会发生什么 昨天和朋友摸鱼,无意之间聊到了执行 DbContext.Table.AddRang ...

  7. EF中加载实体的方式

    EF中的查询执行时机:1. foreach进行枚举2. ToArray.ToList.ToDictionary3. Linq的一些操作,如First.Any4. DbSet上的Load操作.DbEnt ...

  8. Entity Framework入门教程(3)---EF中的上下文简介

    1.DbContext(上下文类) 在DbFirst模式中,我们添加一个EDM(Entity Data Model)后会自动生成一个.edmx文件,这个文件中包含一个继承DbContext类的上下文实 ...

  9. 1.【使用EF Code-First方式和Fluent API来探讨EF中的关系】

    原文链接:http://www.c-sharpcorner.com/UploadFile/3d39b4/relationship-in-entity-framework-using-code-firs ...

随机推荐

  1. 纵表、横表互转的SQL

    纵表.横表互转的SQL By:大志若愚 1.建表: 纵表结构 Table_A  create table Table_A ( 姓名 ), 课程 ), 成绩 int ) ) ) ) ) ) 姓名 课程 ...

  2. 自定义安装php开发环境(1)--apache和php整合

    第一步:安装apache 第二步:下载php核心包php-5.3.3-Win32-VC6-x86.zip.并放入开发环境文件夹C:/phpenv/文件夹下 第三步: 将apache 和php 整合 也 ...

  3. 将Qt5.5 动态链接生成的exe及依赖dll打包方法

    Qt静态编译链接生成的exe文件,不需依赖七大姑八大姨的一堆dll,可以独立运行,发布很方便.但绝大多数用的都是Qt开源版本,如果用静态链接,会有些限制.那有没有办法即能享受静态编译的方便,又不受开源 ...

  4. 20145225 《Java程序设计》 第3周学习总结

    20145225<Java程序设计> 第3周学习总结 教材学习内容总结 4.1类与对象 相当于设计图纸,用"new"创建的对象,就是依据设计图做成的成品 . 例(定义C ...

  5. iOS 支付宝支付

    在开发过程中,经常需要接入第三方支付.下面对支付进行一个概括. 支付宝支付 支付宝SDK下载地址:https://doc.open.alipay.com/doc2/detail?treeId=54&a ...

  6. Java并发(8):CountDownLatch、CyclicBarrier、Semaphore、Callable、Future

    CountDownLatch.CyclicBarrier.Semaphore.Callable.Future  都位于java.util.concurrent包下,其中CountDownLatch.C ...

  7. lvs主备可以自由切换,vip落在主上的时候,端口无法telnet,业务连接不了

    lvs主备可以自由切换,vip落在主上的时候,端口无法telnet,业务连接不了 解决:将主上的keepalived重启,故障解除 原因:不可知 lvs常见故障原因: real server上的脚步没 ...

  8. Win系统查看系统的几个命令

    1. 查看显卡或者显存信息 Win + R dxdiag 2. 查看显卡是独立显卡,或者集成显卡 查看计算机显卡的方法 第一种方法:查看主机连接显示器VGA线连接的接口. 如图:VGA连接线连接在主机 ...

  9. Linux和Windows下查看、设置环境变量的比较

    [一]查看环境变量: 1.windows 查看所有的变量:set    范例:>set    查看某个变量的值:set 环境变量名    范例:     >set JAVA_HOME    ...

  10. CentOS 一个网卡设置多个IP

    方法1:少量IP手动绑定: (这里以绑定IP到eth0为例,其它网卡的话修改相应的文件名即可) 1.复制ifcfg-eth0的网卡配置文件并改名为ifcfg-eth0:0 [root@akinlau ...