从 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,只需(巧妙地)说你不想复制代码 - 你宁愿修复现有的代码以使其可测试。