在XUnit中用Moq怎样模拟EntityFramework Core下的DbSet
最近在做一个项目的单元测试时,遇到了些问题,解决后,觉得有必要记下来,并分享给需要的人,先简单说一下项目技术框架背景:
asp.net core 2.0(for .net core)框架
用Entity Framework Core作ORM
XUnit作单元测试
Moq作隔离框加
在对业务层进行单元测试时,因为业务层调用到数据处理层,所以要用Moq去模拟DbContext,这个很容易做到,但如果操作DbContext下的DbSet和DbSet下的扩展方法时,就会抛出一个System.NotSupportedException异常。这是因为我们没办法Mock DbSet,并助DbSet是个抽象类,还没有办法实例化。
其实,这个时候我们希望的是,如果用一个通用的集合,比如List<T>集合,或T[]数组来Mock DbSet<T>,就非常舒服了,因为集合或数组的元素我们非常容易模拟或控制,不像DbSet。
深挖DbSet下常用的这些扩展方法:Where,Select,SingleOrDefault,FirstOrDefault,OrderBy等,都是对IQueryable的扩展,也就是说把对DbSet的这些扩展方法的调用转成Mock List<T>或T[]的扩展方法调用就OK了,
所以实现下的类型:
项目需要引入:Microsoft.EntityFrameworkCore 和Moq,Nuget可以引入。
UnitTestAsyncEnumerable.cs
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
namespace MoqEFCoreExtension
{
/// <summary>
/// 自定义实现EnumerableQuery<T>, IAsyncEnumerable<T>, IQueryable<T>类型
/// </summary>
/// <typeparam name="T"></typeparam>
class UnitTestAsyncEnumerable<T> : EnumerableQuery<T>, IAsyncEnumerable<T>, IQueryable<T>
{
public UnitTestAsyncEnumerable(IEnumerable<T> enumerable)
: base(enumerable)
{ }
public UnitTestAsyncEnumerable(Expression expression)
: base(expression)
{ }
public IAsyncEnumerator<T> GetEnumerator()
{
return new UnitTestAsyncEnumerator<T>(this.AsEnumerable().GetEnumerator());
}
IQueryProvider IQueryable.Provider
{
get { return new UnitTestAsyncQueryProvider<T>(this); }
}
}
}
UnitTestAsyncEnumerator.cs
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace MoqEFCoreExtension
{
/// <summary>
/// 定义关现IAsyncEnumerator<T>类型
/// </summary>
/// <typeparam name="T"></typeparam>
class UnitTestAsyncEnumerator<T> : IAsyncEnumerator<T>
{
private readonly IEnumerator<T> _inner;
public UnitTestAsyncEnumerator(IEnumerator<T> inner)
{
_inner = inner;
}
public void Dispose()
{
_inner.Dispose();
}
public T Current
{
get
{
return _inner.Current;
}
}
public Task<bool> MoveNext(CancellationToken cancellationToken)
{
return Task.FromResult(_inner.MoveNext());
}
}
}
UnitTestAsyncQueryProvider.cs
using Microsoft.EntityFrameworkCore.Query.Internal;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
namespace MoqEFCoreExtension
{
/// <summary>
/// 实现IQueryProvider接口
/// </summary>
/// <typeparam name="TEntity"></typeparam>
class UnitTestAsyncQueryProvider<TEntity> : IAsyncQueryProvider
{
private readonly IQueryProvider _inner;
internal UnitTestAsyncQueryProvider(IQueryProvider inner)
{
_inner = inner;
}
public IQueryable CreateQuery(Expression expression)
{
return new UnitTestAsyncEnumerable<TEntity>(expression);
}
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
return new UnitTestAsyncEnumerable<TElement>(expression);
}
public object Execute(Expression expression)
{
return _inner.Execute(expression);
}
public TResult Execute<TResult>(Expression expression)
{
return _inner.Execute<TResult>(expression);
}
public IAsyncEnumerable<TResult> ExecuteAsync<TResult>(Expression expression)
{
return new UnitTestAsyncEnumerable<TResult>(expression);
}
public Task<TResult> ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken)
{
return Task.FromResult(Execute<TResult>(expression));
}
}
}
扩展方法类EFSetupData.cs
using Microsoft.EntityFrameworkCore;
using Moq;
using System.Collections.Generic;
using System.Linq;
namespace MoqEFCoreExtension
{
/// <summary>
/// Mock Entity Framework Core中DbContext,加载List<T>或T[]到DbSet<T>
/// </summary>
public static class EFSetupData
{
/// <summary>
/// 加载List<T>到DbSet
/// </summary>
/// <typeparam name="T">实体类型</typeparam>
/// <param name="mockSet">Mock<DbSet>对象</param>
/// <param name="list">实体列表</param>
/// <returns></returns>
public static Mock<DbSet<T>> SetupList<T>(this Mock<DbSet<T>> mockSet, List<T> list) where T : class
{
return mockSet.SetupArray(list.ToArray());
}
/// <summary>
/// 加载数据到DbSet
/// </summary>
/// <typeparam name="T">实体类型</typeparam>
/// <param name="mockSet">Mock<DbSet>对象</param>
/// <param name="array">实体数组</param>
/// <returns></returns>
public static Mock<DbSet<T>> SetupArray<T>(this Mock<DbSet<T>> mockSet, params T[] array) where T : class
{
var queryable = array.AsQueryable();
mockSet.As<IAsyncEnumerable<T>>().Setup(m => m.GetEnumerator()).Returns(new UnitTestAsyncEnumerator<T>(queryable.GetEnumerator()));
mockSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(new UnitTestAsyncQueryProvider<T>(queryable.Provider));
mockSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(queryable.Expression);
mockSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(queryable.ElementType);
mockSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(() => queryable.GetEnumerator());
return mockSet;
}
}
}
var answerSet = new Mock<DbSet<Answers>>().SetupList(list);替换扩展方法,以至于在answerRepository.ModifyAnswer(answer)中调用SingleOrDefault时,操作的是具有两个answers的list,而非DbSet。
源码和Sample:https://github.com/axzxs2001/MoqEFCoreExtension
同时,我把这个功能封闭成了一个Nuget包,参见:https://www.nuget.org/packages/MoqEFCoreExtension/
最后上一个图压压惊:
原文地址:http://www.cnblogs.com/axzxs2001/p/7777311.html
NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注
在XUnit中用Moq怎样模拟EntityFramework Core下的DbSet相关推荐
- EntityFramework Core 1.1有哪些新特性呢?我们需要知道
前言 在项目中用到EntityFramework Core都是现学现用,及时发现问题及时测试,私下利用休闲时间也会去学习其他未曾遇到过或者用过的特性,本节我们来讲讲在EntityFramework C ...
- EntityFramework Core并发导致显式插入主键问题
.NET Core 1.1单元测试问题 我们循序渐进,首先从单元测试开始说起,可能其中就有你在.NET Core上进行单元测试会遇到的问题,别着急,不妨一看.我们需要创建.NET Core类库,,如下 ...
- EntityFramework Core解决并发详解
前言 对过年已经无感,不过还是有很多闲暇时间来学学东西和多陪陪爸妈,这一点是极好的,好了,本节我们来讲讲EntityFramework Core中的并发问题. 话题(EntityFramework C ...
- EntityFramework 6.x和EntityFramework Core必须需要MultipleActiveResultSets?
前言 本节我们来探讨到底需不需要在连接字符串上加上MultipleActiveResultSets = true ?,若您有更深层次的理解欢迎留下您的脚印. EntityFramework 6.x和E ...
- ASP.NET Core Identity自定义数据库结构和完全使用Dapper而非EntityFramework Core
前言 原本本节内容是不存在的,出于有几个人问到了我:我想使用ASP.NET Core Identity,但是我又不想使用默认生成的数据库表,想自定义一套,我想要使用ASP.NE Core Identi ...
- EntityFramework Core查询数据基本本质
[导读]在EntityFramework Core中.当查询出数据后,是如何将数据映射给实体的呢?本节我们预先做个基本探讨,后续给出其底层原理本质 前不久,我们在探索性能时,给出利用反射达到性能瓶颈时 ...
- EntityFramework Core如何映射动态模型?
[导读]本文我们来探讨下映射动态模型的几种方式,相信一部分童鞋项目有这样的需求,比如每天/每小时等生成一张表,此种动态模型映射非常常见,经我摸索,这里给出每一步详细思路,希望能帮助到没有任何头绪的童鞋 ...
- EntityFramework Core 健康检查
[导读].NET Core提供对应方法可进行健康检查,那么在EF Core中是否也提供了相应的方式呢? EF Core 2.2+(包含2.2)版本提供了针对上下文的健康检查,接下来我们直接利用.NET ...
- EntityFramework Core上下文实例池原理
[导读]无论是在我个人博客还是著作中,对于上下文实例池都只是通过大量文字描述来讲解其基本原理,而且也是浅尝辄止,导致我们对其认识仍是一知半解,本文我们摆源码,从源头开始分析 希望通过本文从源码的分析, ...
最新文章
- Java连MySQL性能调优(batch insert和连续left join筛选)
- Android的内存优化的几种方案
- 云南省2021高考成绩查询时间,2021云南高考成绩什么时候几点可以查
- Qt for Android 自定义启动页(解决启动页拉伸的问题)
- Java内置锁——synchronized
- linux看请求报文发送的ip,Linux C 实现最简单的ICMP_ECHO请求报文发送
- maven常用插件: 打包源码 / 跳过测试 / 单独打包依赖项
- win7自定义html为桌面,Win7系统如何自定义个性桌面?
- hprose-php教程,hprose php用户手册
- android日历订阅,Android日历.
- 网易考拉布局和css样式
- element-ui文件上传修改上传文件的格式
- Illegal unquoted character ((CTRL-CHAR, code 13)): has to be escaped using backs
- 「Arm Arch」 ISA 概述
- 康特EPON OLT开局配置
- 逻辑思维强的人适合学计算机不,逻辑思维强的人适合什么工作?
- 火狐浏览器中设置打开新地址时,不会覆盖原页面的方法
- 半钧先生:分享一波超赞的冬至文案,句句暖到心!
- 用CSS实现三角形及其原理
- Swift 头像上传(2)http://blog.csdn.net/wei_chong_chong/article/details/52611110