【问题标题】:Creating a Moq of a static extension method from LLBLGen从 LLBLGen 创建静态扩展方法的 Moq
【发布时间】:2019-04-30 10:10:27
【问题描述】:

我正在尝试使用 Moq 模拟来自 LLBLGen 的 IDataAccessAdapter 接口的扩展方法。这是 FetchQueryAsync 扩展方法。

这样做给了我无法模拟静态扩展方法的错误。但是,我无法更改代码。所以我尝试创建一个包装类,但我也没有成功,因为我不知道如何应用它。

在 Fetch 方法中,我希望 FetchQueryAsync 返回我在测试期间指定的对象,而不是实际执行查询。

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 一起应用来模拟界面?

【问题讨论】:

  • 你为什么要模拟一个扩展方法?你到底想测试什么?

标签: c# unit-testing moq extension-methods llblgen


【解决方案1】:

您从扩展方法开始,然后创建了IWrap 接口以及使用扩展方法的实现。太完美了。

现在你只需要将它注入你的类,就像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 来替换它的具体依赖,那么这可能只是一个关于静态依赖的警示故事。

不过,您有源代码。如果你不能改变现有的类,也许你可以从现有的源代码中创建一个新的。如果有人问您为什么要复制现有的类,只需(委婉地)说您不想复制代码 - 您宁愿修复现有的类,使其可测试。

【讨论】:

  • 我没有意识到包装方法的实际接口应该注入到使用该方法的类中。我认为它可以在单元测试本身中完全处理。然而,这确实提供了一个解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-12
  • 2010-10-26
  • 2015-03-28
  • 2011-05-31
相关资源
最近更新 更多