gpt4 book ai didi

c# - 我如何模拟 FromSql() 方法?

转载 作者:太空狗 更新时间:2023-10-29 21:16:32 27 4
gpt4 key购买 nike

我想知道除了构建一个包装器来模拟 FromSql 之外还有什么方法吗? ?我知道这个方法是静态的,但是因为他们添加了类似 AddEntityFrameworkInMemoryDatabase 的东西对于 Entity Framework 核心,我认为可能也有解决方案,我在我的项目中使用 EF Core 1.0.1。

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

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();
}

我想确保我的查询是用我传递给它的同一对象处理的,基于 this answer在 Entity Framework 6 中,我可以做这样的事情:

[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)));
}

但不像SqlQuery<T>在 EF 6 中,FromSql<T>在 EF Core 中是静态扩展方法,我问这个问题是因为我认为我可能会从错误的角度处理这个问题,或者可能有比包装器更好的替代方法,我会很感激任何对此的想法。

最佳答案

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

来自 this link ,我终于可以写一些单元测试了……

这是我正在测试的类(class):

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你需要添加 Microsoft.EntityFrameworkCore.InMemory来自 NuGet 的包裹辅助类在这里:

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 扩展类..

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。

关于c# - 我如何模拟 FromSql() 方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40726638/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com