[英]Mocking async DbContext functions with input parameter with C#/Moq Framework
I have made a Mock of DbContext and populated with test data. 我做了一个DbContext的模拟,并填充了测试数据。 DbSet is a protected class so the result cannot be mocked, so I found an extension method.
DbSet是一个受保护的类,因此无法模拟结果,因此我找到了扩展方法。
public static class DbSetMocking
{
private static Mock<DbSet<T>> CreateMockSet<T>(IQueryable<T> data)
where T : class
{
var queryableData = data.AsQueryable();
var mockSet = new Mock<DbSet<T>>();
mockSet.As<IQueryable<T>>().Setup(m => m.Provider)
.Returns(queryableData.Provider);
mockSet.As<IQueryable<T>>().Setup(m => m.Expression)
.Returns(queryableData.Expression);
mockSet.As<IQueryable<T>>().Setup(m => m.ElementType)
.Returns(queryableData.ElementType);
mockSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator())
.Returns(queryableData.GetEnumerator());
return mockSet;
}
public static IReturnsResult<TContext> ReturnsDbSet<TEntity, TContext>(this IReturns<TContext, DbSet<TEntity>> setup, TEntity[] entities)
where TEntity : class
where TContext : DbContext
{
var mockSet = CreateMockSet(entities.AsQueryable());
return setup.Returns(mockSet.Object);
}
public static IReturnsResult<TContext> ReturnsDbSet<TEntity, TContext>(this IReturns<TContext, DbSet<TEntity>> setup, IQueryable<TEntity> entities)
where TEntity : class
where TContext : DbContext
{
var mockSet = CreateMockSet(entities);
return setup.Returns(mockSet.Object);
}
public static IReturnsResult<TContext> ReturnsDbSet<TEntity, TContext>(this IReturns<TContext, DbSet<TEntity>> setup, IEnumerable<TEntity> entities)
where TEntity : class
where TContext : DbContext
{
var mockSet = CreateMockSet(entities.AsQueryable());
return setup.Returns(mockSet.Object);
}
}
To create a Mock of DbContext: 创建一个DbContext模拟:
var db = new Mock<DbContext>();
//Populate
this.db.Setup(x => x.MyObjects).ReturnsDbSet(
new List<MyObject>
{
new MyObject{Id=1, Description="Test"},
}
);
Second, I am trying to extend the Mocks to include Find(id) and FindAsync(id) methods. 其次,我正在尝试将Mocks扩展为包括Find(id)和FindAsync(id)方法。 Theese methods are being place in the DbSetMocking class.
这些方法位于DbSetMocking类中。 Find method works fine:
查找方法工作正常:
mockSet.Setup(m => m.Find(It.IsAny<object[]>()))
.Returns<object[]>(id => StaticMethodtoFindStuff<T>(queryableData, id));
However, I cannot get the FindAsync method to work. 但是,我无法使FindAsync方法正常工作。 This is what I have tried so far:
到目前为止,这是我尝试过的:
mockSet.Setup(m => m.FindAsync(It.IsAny<object[]>()))
.Returns(Task.FromResult(StaticMethodtoFindStuff<T>(queryableData, 1)));
This one works, but then I dont have access to the parameter set by the function. 此功能有效,但随后我无法访问该函数设置的参数。 Tried this one, compiling works fine, but it fails on execution with the error msg:
尝试了这一操作,编译工作正常,但执行失败,并显示错误msg:
Object of type 'System.Object[]' cannot be converted to type 'System.Threading.Tasks.Task`1[System.Object[]]'. 类型'System.Object []'的对象不能转换为类型'System.Threading.Tasks.Task`1 [System.Object []]'。
mockSet.Setup(m => m.FindAsync(It.IsAny<object[]>()))
.Returns<Task<object[]>>(d =>
{
return Task.FromResult(StaticMethodtoFindStuff<T>(queryableData, d));
});
Anybody have a clue how I can implement this function? 有人知道如何实现此功能吗?
Try changing this line: 尝试更改此行:
mockSet.Setup(m => m.FindAsync(It.IsAny<object[]>()))
.Returns<Task<object[]>>(d =>
{
return Task.FromResult(StaticMethodtoFindStuff<T>(queryableData, d));
});
to: 至:
mockSet.Setup(m => m.FindAsync(It.IsAny<object[]>()))
.Returns<object[]>(d =>
{
return Task.FromResult(StaticMethodtoFindStuff<T>(queryableData, d));
});
The thing here is, generic argument T
for .Returns<T>
is not the type of result, but instead, that's the type of first parameter, passed inside function in .Setup
method -- for your it's object[]. 这里的问题是,
.Returns<T>
通用参数T
不是结果的类型,而是那是在.Setup
方法中传递给函数的第一个参数的类型-对于您的对象是[]。
Check Moq source code for more info: 查看Moq源代码以获取更多信息:
https://github.com/moq/moq4/blob/b2cf2d303ea6644fda2eaf10bad43c88c05b395f/Source/Language/IReturns.Generated.cs https://github.com/moq/moq4/blob/b2cf2d303ea6644fda2eaf10bad43c88c05b395f/Source/Language/IReturns.Generated.cs
Here's a quote from the sources, check T1 and T2 parameter commentaries: 这是来源的引文,请检查T1和T2参数注释:
/// <summary>
/// Specifies a function that will calculate the value to return from the method,
/// retrieving the arguments for the invocation.
/// </summary>
/// <typeparam name="T1">The type of the first argument of the invoked method.</typeparam>
/// <typeparam name="T2">The type of the second argument of the invoked method.</typeparam>
/// <param name="valueFunction">The function that will calculate the return value.</param>
/// <return>Returns a calculated value which is evaluated lazily at the time of the invocation.</return>
/// <example>
/// <para>
/// The return value is calculated from the value of the actual method invocation arguments.
/// Notice how the arguments are retrieved by simply declaring them as part of the lambda
/// expression:
/// </para>
/// <code>
/// mock.Setup(x => x.Execute(
/// It.IsAny<int>(),
/// It.IsAny<int>()))
/// .Returns((int arg1, int arg2) => arg1 + arg2); //I fixed that line, it's different in the documentation and is incorrect
/// </code>
/// </example>
IReturnsResult<TMock> Returns<T1, T2>(Func<T1, T2, TResult> valueFunction);
You can also see, that it defines multiple overloads of Returns with variable number of generic parameters, so that you could mock a method with up to 16 parameters. 您还可以看到,它使用可变数目的泛型参数定义了多个Returns重载,因此您可以模拟最多包含16个参数的方法。
Finally got it figured out. 终于弄明白了。 Turned out that I was missing an 'async' keyword there.
原来我在那里缺少一个“异步”关键字。 Code is:
代码是:
mockSet.Setup(m => m.FindAsync(It.IsAny<object[]>()))
.Returns<object[]>(async (d) =>
{
return await Task.FromResult(StaticMethodtoFindStuff<T>(queryableData, d));
});
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.