如何模拟FromSql()方法?

编程入门 行业动态 更新时间:2024-10-27 10:31:51
本文介绍了如何模拟FromSql()方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述

我想知道除了构建用于模拟FromSql的包装器之外,还有其他方法吗?我知道这种方法是静态的,但是由于他们在实体框架核心中添加了AddEntityFrameworkInMemoryDatabase之类的东西,因此我认为可能也有解决方案,我在我的项目中使用了EF Core 1.0.1.

I was wondering is there any way other than building a wrapper for mocking the FromSql? I know this method is static, but since they added things like AddEntityFrameworkInMemoryDatabase to entity framework core, I thought there might be a solution for this too, I use EF Core 1.0.1 in my project.

我的最终目标是测试此方法:

My end goal is to test this method:

public List<Models.ClosestLocation> Handle(ClosestLocationsQuery message) { return _context.ClosestLocations.FromSql( "EXEC GetClosestLocations {0}, {1}, {2}, {3}", message.LocationQuery.Latitude, message.LocationQuery.Longitude, message.LocationQuery.MaxRecordsToReturn ?? 10, message.LocationQuery.Distance ?? 10 ).ToList(); }

我想根据此答案,确保使用与查询相同的对象来处理我的查询.在实体框架6中,我可以执行以下操作:

I want to ensure that my query is handled with the same object that I passed into it, based on this answer in entity framework 6 I could do something like this:

[Fact] public void HandleInvokesGetClosestLocationsWithCorrectData() { var message = new ClosestLocationsQuery { LocationQuery = new LocationQuery {Distance = 1, Latitude = 1.165, Longitude = 1.546, MaxRecordsToReturn = 1} }; var dbSetMock = new Mock<DbSet<Models.ClosestLocation>>(); dbSetMock.Setup(m => m.FromSql(It.IsAny<string>(), message)) .Returns(It.IsAny<IQueryable<Models.ClosestLocation>>()); var contextMock = new Mock<AllReadyContext>(); contextMock.Setup(c => c.Set<Models.ClosestLocation>()).Returns(dbSetMock.Object); var sut = new ClosestLocationsQueryHandler(contextMock.Object); var results = sut.Handle(message); contextMock.Verify(x => x.ClosestLocations.FromSql(It.IsAny<string>(), It.Is<ClosestLocationsQuery>(y => y.LocationQuery.Distance == message.LocationQuery.Distance && y.LocationQuery.Latitude == message.LocationQuery.Latitude && y.LocationQuery.Longitude == message.LocationQuery.Longitude && y.LocationQuery.MaxRecordsToReturn == message.LocationQuery.MaxRecordsToReturn))); }

但是与EF 6中的SqlQuery<T>不同,EF Core中的FromSql<T>是静态扩展方法,我问这个问题是因为我认为我可能会从错误的角度解决此问题,或者可能有比这更好的选择包装材料,对此我将不胜感激.

But unlike SqlQuery<T> in EF 6, the FromSql<T> in EF Core is static extension method, I'm asking this question because I think I might approach this problem from the wrong angle or there might be a better alternative than a wrapper, I'd appreciate any thought on this.

推荐答案

我也遇到了同样的情况,Philippe给出的答案也有所帮助,但主要方法是抛出System.ArgumentNullException.

I also fell into the same situation and answer given by Philippe helped but it the main method was throwing System.ArgumentNullException.

来自此链接 ,我终于可以写一些单元测试了……

From this link, I was finally able to write some unit tests...

这是我的课程正在测试:

Here is my class under test:

public class HolidayDataAccess : IHolidayDataAccess { private readonly IHolidayDataContext holDataContext; public HolidayDataAccess(IHolidayDataContext holDataContext) { this.holDataContext = holDataContext; } public async Task<IEnumerable<HolidayDate>> GetHolidayDates(DateTime startDate, DateTime endDate) { using (this.holDataContext) { IList<HolidayDate> dates = await holDataContext.Dates.FromSql($"[dba].[usp_GetHolidayDates] @StartDate = {startDate}, @EndDate = {endDate}").AsNoTracking().ToListAsync(); return dates; } } }

这是测试方法:

[TestMethod] public async Task GetHolidayDates_Should_Only_Return_The_Dates_Within_Given_Range() { // Arrange. SpAsyncEnumerableQueryable<HolidayDate> dates = new SpAsyncEnumerableQueryable<HolidayDate>(); dates.Add(new HolidayDate() { Date = new DateTime(2018, 05, 01) }); dates.Add(new HolidayDate() { Date = new DateTime(2018, 07, 01) }); dates.Add(new HolidayDate() { Date = new DateTime(2018, 04, 01) }); dates.Add(new HolidayDate() { Date = new DateTime(2019, 03, 01) }); dates.Add(new HolidayDate() { Date = new DateTime(2019, 02, 15) }); var options = new DbContextOptionsBuilder<HolidayDataContext>() .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) .Options; HolidayDataContext context = new HolidayDataContext(options); context.Dates = context.Dates.MockFromSql(dates); HolidayDataAccess dataAccess = new HolidayDataAccess(context); //Act. IEnumerable<HolidayDate> resutlDates = await dataAccess.GetHolidayDates(new DateTime(2018, 01, 01), new DateTime(2018, 05, 31)); // Assert. Assert.AreEqual(resutlDates.Any(d => d.Date != new DateTime(2019, 03, 01)), true); Assert.AreEqual(resutlDates.Any(d => d.Date != new DateTime(2019, 02, 15)), true); // we do not need to call this becuase we are using a using block for the context... //context.Database.EnsureDeleted(); }

要使用UseInMemoryDatabase,您需要从 NuGet 辅助程序类在这里:

To use UseInMemoryDatabase you need to add Microsoft.EntityFrameworkCore.InMemory package from NuGet The helper classes are here:

public class SpAsyncEnumerableQueryable<T> : IAsyncEnumerable<T>, IQueryable<T> { private readonly IList<T> listOfSpReocrds; public Type ElementType => throw new NotImplementedException(); public IQueryProvider Provider => new Mock<IQueryProvider>().Object; Expression IQueryable.Expression => throw new NotImplementedException(); public SpAsyncEnumerableQueryable() { this.listOfSpReocrds = new List<T>(); } public void Add(T spItem) // this is new method added to allow xxx.Add(new T) style of adding sp records... { this.listOfSpReocrds.Add(spItem); } public IEnumerator<T> GetEnumerator() { return this.listOfSpReocrds.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } IAsyncEnumerator<T> IAsyncEnumerable<T>.GetEnumerator() { return listOfSpReocrds.ToAsyncEnumerable().GetEnumerator(); } }

...以及包含FromSql方法的模拟的Db扩展类.

...and the Db extensions class that contains the mock of FromSql method..

public static class DbSetExtensions { public static DbSet<T> MockFromSql<T>(this DbSet<T> dbSet, SpAsyncEnumerableQueryable<T> spItems) where T : class { var queryProviderMock = new Mock<IQueryProvider>(); queryProviderMock.Setup(p => p.CreateQuery<T>(It.IsAny<MethodCallExpression>())) .Returns<MethodCallExpression>(x => spItems); var dbSetMock = new Mock<DbSet<T>>(); dbSetMock.As<IQueryable<T>>() .SetupGet(q => q.Provider) .Returns(() => queryProviderMock.Object); dbSetMock.As<IQueryable<T>>() .Setup(q => q.Expression) .Returns(Expression.Constant(dbSetMock.Object)); return dbSetMock.Object; } }

希望这会有所帮助!

编辑:将SpAsyncEnumerableQueryable类重构为具有Add方法.摆脱了采用T数组的参数化构造.实现了IQueryProvider Provider => new Mock<IQueryProvider>().Object;以支持.AsNoTracking().异步调用ToList.

Edits: refactored SpAsyncEnumerableQueryable class to have Add method. Got rid of parameterised construction that took array of T. Implemented IQueryProvider Provider => new Mock<IQueryProvider>().Object; to support .AsNoTracking(). Calling the ToList asynchronously.

更多推荐

如何模拟FromSql()方法?

本文发布于:2023-11-08 16:30:28,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1569886.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:方法   FromSql

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!