从 LLBLGen 创建静态扩展方法的 Moq
Creating a Moq of a static extension method from LLBLGen
我正在尝试使用 Moq 从 LLBLGen 的 IDataAccessAdapter 接口模拟扩展方法。这是 FetchQueryAsync 扩展方法。
这样做给了我无法模拟静态扩展方法的错误。但是我无法更改代码。所以我尝试创建一个包装器 class,但我也没有成功,因为我不知道如何应用它。
在 Fetch 方法中,我希望 FetchQueryAsync return 我在测试期间指定的对象,而不是实际执行查询。
public class QueryHandler
{
private IDataAccessAdapterProvider dataAccessAdapterProvider;
public QueryHandler(IDataAccessAdapterProvider provider)
{
this.dataAccessAdapterProvider = provider;
}
private async Task<T> Fetch(DynamicQuery<T> query)
{
using (IDataAccessAdapter adapter = dataAccessAdapterProvider.Provide()
{
result = await adapter.FetchQueryAsync(query)
}
}
}
public class DataAccessAdapterProvider : IDataAccessAdapterProvider
{
public IDataAccessAdapter Provide()
{
var adapter = new DataAccessAdapter();
return adapter;
}
}
所以在我的单元测试中我有这个:
List<int> il = new List<int>();
Mock<IDataAccessAdapterProvider> mock = new Mock<IDataAccessAdapterProvider>();
mock.Setup(m => m.Provide()
.FetchQueryAsync<int>(It.IsAny<DynamicQuery<int>>()))
.ReturnsAsync(il);
但这不会起作用,因为它不受支持。
所以我尝试这样包装方法。
interface IWrap
{
Task<List<TElement>> FetchQueryAsync<TElement>(IDataAccessAdapter adapter, DynamicQuery<TElement> query);
}
public class Wrap : IWrap
{
public async Task<List<TElement>> FetchQueryAsync<TElement>(IDataAccessAdapter adapter, DynamicQuery<TElement> query)
{
return await adapter.FetchQueryAsync(query);
}
}
我如何将此包装器与 Moq 一起应用于模拟界面?
您从扩展方法开始,然后创建了 IWrap
接口以及使用扩展方法的实现。太完美了。
现在您只需将它注入您的 class,就像 IDataAccessAdapterProvider
已经注入一样:
public class QueryHandler
{
private readonly IDataAccessAdapterProvider _dataAccessAdapterProvider;
private readonly IWrap _wrap; //I'm assuming you'll want a different name.
public QueryHandler(IDataAccessAdapterProvider provider, IWrap wrap)
{
_dataAccessAdapterProvider = provider;
_wrap = wrap;
}
(我在那里应用了一个通用约定。在字段名称前加上下划线 - _wrap
- 意味着字段和构造函数参数具有不同的名称,因此您无需指定 this.wrap
。另外,当人们在其他地方看到下划线时,他们会知道这是一个字段。)
现在你可以模拟界面了:
var mock = new Mock<IWrap>();
var returnedFromMock = new List<int> { 1, 2, 3 };
mock.Setup(x => x.FetchQueryAsync<int>(It.IsAny<IDataAccessAdapter>(), It.IsAny<DynamicQuery<int>>()))
.ReturnsAsync(returnedFromMock);
您提到您无法更改密码。我不确定你不能更改哪一部分,但如果你不能更改 QueryHandler
来替换它的具体依赖项,那么这可能只是一个关于静态依赖项的警示故事。
不过你有源代码。如果您不能更改现有的 class,也许您可以从现有的源代码创建一个新的。如果有人问你为什么要复制现有的 class,只需(巧妙地)说你不想复制代码 - 你宁愿修复现有的代码以使其可测试。
我正在尝试使用 Moq 从 LLBLGen 的 IDataAccessAdapter 接口模拟扩展方法。这是 FetchQueryAsync 扩展方法。
这样做给了我无法模拟静态扩展方法的错误。但是我无法更改代码。所以我尝试创建一个包装器 class,但我也没有成功,因为我不知道如何应用它。
在 Fetch 方法中,我希望 FetchQueryAsync return 我在测试期间指定的对象,而不是实际执行查询。
public class QueryHandler
{
private IDataAccessAdapterProvider dataAccessAdapterProvider;
public QueryHandler(IDataAccessAdapterProvider provider)
{
this.dataAccessAdapterProvider = provider;
}
private async Task<T> Fetch(DynamicQuery<T> query)
{
using (IDataAccessAdapter adapter = dataAccessAdapterProvider.Provide()
{
result = await adapter.FetchQueryAsync(query)
}
}
}
public class DataAccessAdapterProvider : IDataAccessAdapterProvider
{
public IDataAccessAdapter Provide()
{
var adapter = new DataAccessAdapter();
return adapter;
}
}
所以在我的单元测试中我有这个:
List<int> il = new List<int>();
Mock<IDataAccessAdapterProvider> mock = new Mock<IDataAccessAdapterProvider>();
mock.Setup(m => m.Provide()
.FetchQueryAsync<int>(It.IsAny<DynamicQuery<int>>()))
.ReturnsAsync(il);
但这不会起作用,因为它不受支持。 所以我尝试这样包装方法。
interface IWrap
{
Task<List<TElement>> FetchQueryAsync<TElement>(IDataAccessAdapter adapter, DynamicQuery<TElement> query);
}
public class Wrap : IWrap
{
public async Task<List<TElement>> FetchQueryAsync<TElement>(IDataAccessAdapter adapter, DynamicQuery<TElement> query)
{
return await adapter.FetchQueryAsync(query);
}
}
我如何将此包装器与 Moq 一起应用于模拟界面?
您从扩展方法开始,然后创建了 IWrap
接口以及使用扩展方法的实现。太完美了。
现在您只需将它注入您的 class,就像 IDataAccessAdapterProvider
已经注入一样:
public class QueryHandler
{
private readonly IDataAccessAdapterProvider _dataAccessAdapterProvider;
private readonly IWrap _wrap; //I'm assuming you'll want a different name.
public QueryHandler(IDataAccessAdapterProvider provider, IWrap wrap)
{
_dataAccessAdapterProvider = provider;
_wrap = wrap;
}
(我在那里应用了一个通用约定。在字段名称前加上下划线 - _wrap
- 意味着字段和构造函数参数具有不同的名称,因此您无需指定 this.wrap
。另外,当人们在其他地方看到下划线时,他们会知道这是一个字段。)
现在你可以模拟界面了:
var mock = new Mock<IWrap>();
var returnedFromMock = new List<int> { 1, 2, 3 };
mock.Setup(x => x.FetchQueryAsync<int>(It.IsAny<IDataAccessAdapter>(), It.IsAny<DynamicQuery<int>>()))
.ReturnsAsync(returnedFromMock);
您提到您无法更改密码。我不确定你不能更改哪一部分,但如果你不能更改 QueryHandler
来替换它的具体依赖项,那么这可能只是一个关于静态依赖项的警示故事。
不过你有源代码。如果您不能更改现有的 class,也许您可以从现有的源代码创建一个新的。如果有人问你为什么要复制现有的 class,只需(巧妙地)说你不想复制代码 - 你宁愿修复现有的代码以使其可测试。