在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跨平台或扫描二维码关注

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/323046.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

解决idea中xml文件报红问题

报错截图&#xff1a; 解决方法&#xff1a; 复制爆红的约束链接file >>> settings >>> Languages & Frameworks >>> Schemas and DTDs右边框中找到 Ignored schemas and DTD3:点击加号添加约束&#xff0c;如下图应用即可&#xff01;

Hadoop入门(四)HDFS编程

一、编程环境设置 编程使用到idea2018&#xff0c;maven &#xff08;1&#xff09;启动集群 在window上启动vmware软件的虚拟机hadoop01&#xff0c;hadoop02&#xff0c;hadoop03。 进入hadoop01虚拟机启动集群&#xff0c;执行命令 start-dfs.sh &#xff08;2&#x…

ASP.NET Core 认证与授权[4]:JwtBearer认证

Bearer认证 HTTP提供了一套标准的身份验证框架&#xff1a;服务器可以用来针对客户端的请求发送质询(challenge)&#xff0c;客户端根据质询提供身份验证凭证。质询与应答的工作流程如下&#xff1a;服务器端向客户端返回401&#xff08;Unauthorized&#xff0c;未授权&#x…

隧道裂缝检测_2【C++PCL】

作者:迅卓科技 简介:本人从事过多项点云项目,并且负责的项目均已得到好评! 公众号:迅卓科技,一个可以让您可以学习点云的好地方 1.前言 我们团队注重每一个细节,确保代码的可读性、可维护性和可扩展性达到最高标准。我们严格遵循行业最佳实践,采用模块化和面向对象的设…

开源纯C#工控网关+组态软件(五)从网关到人机界面

一、 引子 之前都在讲网关&#xff0c;不少网友关注如何实现界面。想了解下位机变量变化&#xff0c;是怎样一步步触发人机界面动画的。 这个步步触发&#xff0c;实质上是变量组&#xff08;Group&#xff09;的批量数据变化&#xff08;DataChange&#xff09;事件&#xf…

P1315,jzoj3029-观光公交【费用流】

前言 你绝对想不到&#xff0c;我用费用流神仙构图做了一道 的题 正题 评测记录:https://www.luogu.org/recordnew/lists?uid52918&pidP1315 题目大意 有nnn个地方&#xff0c;iii到第i1i1i1的长度为did_idi​。 有mmm个人&#xff0c;从tit_iti​出发&#xff0c;从l…

Spring依赖注入和控制反转

文章目录1、依赖注入1.1、依赖注入和控制反转的概念1.2、依赖注入的实现方式1.3、控制反转的具体实现1.4、依赖注入的具体实现1.5、依赖注入和控制反转总结1、依赖注入 1.1、依赖注入和控制反转的概念 依赖注入(Dependency Injection, 简称DI)与控制反转(IoC)的含义相同&…

Hadoop入门(五)IO操作

一、HadoopIO操作意义 Hadoop自带一套用于I/O的原子性的操作 &#xff08;不会被线程调度机制打断&#xff0c;一直到结束&#xff0c;中间不会有任何context switch&#xff09; 特点 基于保障海量数据集的完整性和压缩性 Hadoop提供了一些用于开发分布式系统的API&#xff…

使用Identity Server 4建立Authorization Server (1)

预备知识: 学习Identity Server 4的预备知识 本文内容基本完全来自于Identity Server 4官方文档: https://identityserver4.readthedocs.io/ 官方文档很详细的. 使用OAuth可以更安全, 这里我们的authorization server和web api 以及网站将分别独立运行. 建立authorization ser…

Python和SQL Server 2017的力量

Python是SQL Server 2017的新功能。 它主要是为了允许在SQL Server中使用基于Python的机器学习&#xff0c;但是它可以与任何Python库或框架一起使用。为了提供可能的例子&#xff0c;Hitendra展示了如何安全地使用该功能来提供智能应用程序缓存&#xff0c;其中SQL Server可以…

Spring中的Bean配置、属性配置、装配内容详细叙述

文章目录1、Bean的配置1.1、配置方式2、Bean的实例化2.1、构造器实例化2.2、静态工厂方式实例化2.3、实例工厂方式实例化3、Bean的作用域3.1、作用域的种类4、Bean的生命周期5、Bean的装配方式5.1、基于XML的装配5.2、基于Annotation的装配5.3、自动装配1、Bean的配置 1.1、配…

Hadoop入门(六)Mapreduce

一、Mapreduce概述 MapReduce是一个编程模型&#xff0c;用以进行大数据量的计算 二、Hadoop MapReduce &#xff08;1&#xff09;MapReduce是什么 Hadoop MapReduce是一个软件框架&#xff0c;基于该框架能够容易地编写应用程序&#xff0c;这些应用程序能够运行在由上千个…

Ocelot API网关的实现剖析

在微软Tech Summit 2017 大会上和大家分享了一门课程《.NET Core 在腾讯财付通的企业级应用开发实践》&#xff0c;其中重点是基于ASP.NET Core打造可扩展的高性能企业级API网关&#xff0c;以开源的API网关Ocelot为基础结合自己的业务特性&#xff0c;当天课程只有40分钟&…

Hadoop入门(十二)Intellij IDEA远程向hadoop集群提交mapreduce作业

Intellij IDEA远程向hadoop集群提交mapreduce作业&#xff0c;需要依赖到hadoop的库&#xff0c;hadoop集群的配置信息&#xff0c;还有本地项目的jar包。 一、软件环境 &#xff08;1&#xff09;window本地安装hadoop软件 首先将集群上的hadoop环境下载到本地&#xff0c;…

Spring AOP知识点简介

文章目录1、什么是AOP1.1、AOP术语1.2、AOP框架2、动态代理2.1、JDK动态代理2.2、CGLIB动态代理3、基于代理类的AOP实现3.1、Spring的通知类型3.2、ProxyFactoryBean4、AspectJ开发4.1、基于XML的声明式AspectJ4.2、基于注解的声明式AspectJ1、什么是AOP 面向切面编程&#xf…

SQL2017 Azure SQL新功能:图形数据库

图形数据库是什么呢&#xff1f;如果从字面理解是进行图形处理的数据库&#xff0c;那么你就错了哈哈。 我们先来解释什么是图形数据库。 图形数据库是NoSQL数据库的一种类型&#xff0c;它应用图形理论存储实体之间的关系信息。最常见的例子&#xff0c;就是社会网络中人与人之…

Hadoop入门(十三)远程提交wordCout程序到hadoop集群

一、项目结构 用到的文件有WordCount.java、core-site.xml、mapreduce-site.xml、yarn-site.xml、log4j.properties、pom.xml 二、项目源码 &#xff08;1&#xff09;WordCount.java package com.mk.mapreduce;import org.apache.hadoop.conf.Configuration; import org.ap…

腾讯云短信服务使用记录与.NET Core C#代码分享

1、即使是相同的短信签名与短信正文模板&#xff0c;也需要针对“国内文本短信”与“海外文本短信”分别申请。开始不知道&#xff0c;以为只要申请一次&#xff0c;给国外手机发短信时给api传对应的国家码就行&#xff0c;后来才发现需要分别申请。 2、短信服务web api响应“手…

Hadoop入门(九)Mapreduce高级shuffle之Combiner

一、Combiner的出现 &#xff08;1&#xff09;为什么需要进行Map规约操 作 在上述过程中&#xff0c;我们看到至少两个性能瓶颈&#xff1a; &#xff08;1&#xff09;如果我们有10亿个数据&#xff0c;Mapper会生成10亿个键值对在网络间进行传输&#xff0c;但如果我们只…

欢乐纪中某A组赛【2019.1.19】

前言 因为BBB有一堆(两道)题都做过&#xff0c;于是就来做A组了。 成绩 RankRankRank是有算别人的 RankRankRankPersonPersonPersonScoreScoreScoreAAABBBCCC3332017myself2017myself2017myself2102102101001001001001001001010102222222017lrz2017lrz2017lrz1001001000001001…