为喜欢的事早起一个小时

小说:为喜欢的事早起一个小时作者:平安帝更新时间:2019-03-20字数:29469

选手们的入口和观众入口处于相反的位置,不过青年歌手大赛节目中负责接送选手的车太显眼,他坐着车途径观众视野时,有眼尖的观众认出了他,现场立即尖叫连连。

如何应对宝宝的第一个叛逆期!

九曜星君此时早回了殿中,太白金星道:“上次老君将其投入八卦炉,困了四百年仍被妖猴逃出,此番又有谁能制他?”
从韩非开始出手,到消灭这八个鬼子,总共花了不到两三分钟时间,场面惊险得很,看得王先生和他那两个手下口瞪目呆,直到韩非拉了他一把,他才醒悟过来,战斗结束了。

“呵呵,痛苦吗?你那是什么狗屁的理想,真正带来不和平,破坏和平的人就是你这种垃圾。”布玛嘶声说道,手中出现了两把苦无,白虎知道怎么做,一下子将其中一把破坏掉,天道佩恩看到两把苦无的时候已经知道不好了,只可惜想退都已经晚了。

最近在做一个项目的单元测试时,遇到了些问题,解决后,觉得有必要记下来,并分享给需要的人,先简单说一下项目技术框架背景:

  • 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

 1 using System.Collections.Generic;
 2 using System.Linq;
 3 using System.Linq.Expressions;
 4 
 5 namespace MoqEFCoreExtension
 6 {
 7     /// <summary>
 8     /// 自定义实现EnumerableQuery<T>, IAsyncEnumerable<T>, IQueryable<T>类型
 9     /// </summary>
10     /// <typeparam name="T"></typeparam>
11     class UnitTestAsyncEnumerable<T> : EnumerableQuery<T>, IAsyncEnumerable<T>, IQueryable<T>
12     {
13         public UnitTestAsyncEnumerable(IEnumerable<T> enumerable)
14             : base(enumerable)
15         { }
16 
17         public UnitTestAsyncEnumerable(Expression expression)
18             : base(expression)
19         { }
20 
21         public IAsyncEnumerator<T> GetEnumerator()
22         {
23             return new UnitTestAsyncEnumerator<T>(this.AsEnumerable().GetEnumerator());
24         }
25 
26         IQueryProvider IQueryable.Provider
27         {
28             get { return new UnitTestAsyncQueryProvider<T>(this); }
29         }
30     }
31 }

UnitTestAsyncEnumerator.cs

 1 using System.Collections.Generic;
 2 using System.Threading;
 3 using System.Threading.Tasks;
 4 
 5 namespace MoqEFCoreExtension
 6 {
 7     /// <summary>
 8     /// 定义关现IAsyncEnumerator<T>类型
 9     /// </summary>
10     /// <typeparam name="T"></typeparam>
11     class UnitTestAsyncEnumerator<T> : IAsyncEnumerator<T>
12     {
13         private readonly IEnumerator<T> _inner;
14 
15         public UnitTestAsyncEnumerator(IEnumerator<T> inner)
16         {
17             _inner = inner;
18         }
19 
20         public void Dispose()
21         {
22             _inner.Dispose();
23         }
24 
25         public T Current
26         {
27             get
28             {
29                 return _inner.Current;
30             }
31         }
32 
33         public Task<bool> MoveNext(CancellationToken cancellationToken)
34         {
35             return Task.FromResult(_inner.MoveNext());
36         }
37     }
38 }

UnitTestAsyncQueryProvider.cs

 1 using Microsoft.EntityFrameworkCore.Query.Internal;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Linq.Expressions;
 5 using System.Threading;
 6 using System.Threading.Tasks;
 7 
 8 namespace MoqEFCoreExtension
 9 {
10     /// <summary>
11     /// 实现IQueryProvider接口
12     /// </summary>
13     /// <typeparam name="TEntity"></typeparam>
14     class UnitTestAsyncQueryProvider<TEntity> : IAsyncQueryProvider
15     {
16         private readonly IQueryProvider _inner;
17 
18         internal UnitTestAsyncQueryProvider(IQueryProvider inner)
19         {
20             _inner = inner;
21         }
22 
23         public IQueryable CreateQuery(Expression expression)
24         {
25             return new UnitTestAsyncEnumerable<TEntity>(expression);
26         }
27 
28         public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
29         {
30             return new UnitTestAsyncEnumerable<TElement>(expression);
31         }
32 
33         public object Execute(Expression expression)
34         {
35             return _inner.Execute(expression);
36         }
37 
38         public TResult Execute<TResult>(Expression expression)
39         {
40             return _inner.Execute<TResult>(expression);
41         }
42 
43         public IAsyncEnumerable<TResult> ExecuteAsync<TResult>(Expression expression)
44         {
45             return new UnitTestAsyncEnumerable<TResult>(expression);
46         }
47 
48         public Task<TResult> ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken)
49         {
50             return Task.FromResult(Execute<TResult>(expression));
51         }
52     }
53 }

扩展方法类EFSetupData.cs

 1 using Microsoft.EntityFrameworkCore;
 2 using Moq;
 3 using System.Collections.Generic;
 4 using System.Linq;
 5 
 6 
 7 namespace MoqEFCoreExtension
 8 {
 9     /// <summary>
10     /// Mock Entity Framework Core中DbContext,加载List<T>或T[]到DbSet<T>
11     /// </summary>
12     public static class EFSetupData
13     {
14         /// <summary>
15         /// 加载List<T>到DbSet
16         /// </summary>
17         /// <typeparam name="T">实体类型</typeparam>
18         /// <param name="mockSet">Mock<DbSet>对象</param>
19         /// <param name="list">实体列表</param>
20         /// <returns></returns>
21         public static Mock<DbSet<T>> SetupList<T>(this Mock<DbSet<T>> mockSet, List<T> list) where T : class
22         {
23             return mockSet.SetupArray(list.ToArray());
24         }
25         /// <summary>
26         /// 加载数据到DbSet
27         /// </summary>
28         /// <typeparam name="T">实体类型</typeparam>
29         /// <param name="mockSet">Mock<DbSet>对象</param>
30         /// <param name="array">实体数组</param>
31         /// <returns></returns>
32         public static Mock<DbSet<T>> SetupArray<T>(this Mock<DbSet<T>> mockSet, params T[] array) where T : class
33         {
34             var queryable = array.AsQueryable();
35             mockSet.As<IAsyncEnumerable<T>>().Setup(m => m.GetEnumerator()).Returns(new UnitTestAsyncEnumerator<T>(queryable.GetEnumerator()));
36             mockSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(new UnitTestAsyncQueryProvider<T>(queryable.Provider));
37             mockSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(queryable.Expression);
38             mockSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(queryable.ElementType);
39             mockSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(() => queryable.GetEnumerator());
40             return mockSet;
41         }
42     }
43 }

  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/

最后上一个图压压惊:

 

编辑:侯安陵成

发布:2019-03-20 17:18:00

当前文章:http://www.emigration.net.cn/jsjtx.html

一只狗的绝望体验 如何向心爱之人表白 罗李华谈:属猪的人2016年运程 低龄儿童培养的是领导力还是全能感? 【心理咨询】心理问题严重的男孩 我为何偷了4年的胸罩? 一个没带手机的下午,我是这样度过的 谁为你的人生买单

15123 75737 76754 50143 72742 6003656587 78298 60562

我要说两句: (0人参与)

发布