日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

在XUnit中用Moq怎样模拟EntityFramework Core下的DbSet

發布時間:2023/12/4 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 在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的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。