使用Linq表达式和Lambdas测试的存根代码

本文关键字:存根 代码 测试 Lambdas Linq 表达式 使用 | 更新日期: 2023-09-27 18:00:30

我的代码中有一个查询表达式,它执行以下操作:

repository.Context.AsQueryable<Option>().Where(o => o.Id == id && o.Name == "Something").Select(o => o.Id).ToArray();

我将如何为上面的代码创建一个Stub?看起来工作量很大。我是否可以简单地忽略传递给where和Select方法的内容,然后返回我想要返回的内容?

我并不关心在Where和Select方法中传递了什么。我只想最后返回我的硬编码项目列表。

使用Linq表达式和Lambdas测试的存根代码

作为一个选项,使用您的代码作为依赖项。通过这种方式,您可以在根本不涉及上下文的情况下将其截断。例如:

public class OptionService : IOptionService
{
    private IRepository _repository;
    public OptionService(IRepository repository)
    {
        _repository = repository;
    }
    public int[] GetOptionsWithName(int id, string name)
    {
        _repository.Context.AsQueryable<Option>()
                           .Where(o => o.Id == id && o.Name == name)
                           .Select(o => o.Id)
                           .ToArray();
    }
}
public interface IOptionService
{
    int[] GetOptionsWithName(int id, string name);
}

用类似的逻辑将IOptionService注入到代码中,IRepository是如何注入到OptionService中的,并在测试中存根方法GetOptionsWithName以返回您想要的任何内容。

简短的回答是:不,因为.Where()和.Select()是扩展方法,不能被嘲笑。

较长的答案是:是的,因为IQueryable<>上的.Where().Select()只会向底层查询提供程序指示它们刚刚被调用。因此,从技术上讲,您可以为查询提供程序创建一个存根,并在评估它之前查看它发生了什么。

但简单的答案是:我发现最好的方法是使用一个实际的内存表示,它能够像列表一样执行可查询的操作。然后,与其尝试验证lambda表达式本身,不如测试生成的数据。

var options = new[] {new Option(...)};
repositoryMock.Setup(r => r.Context).Returns(contextMock.Object);
contextMock.Setup(c => c.AsQueryable<Option>()).Returns(options.AsQueryable());
...
Assert.AreEqual(results[0], options[0].Id);

这样做的缺点是,无法测试您的方法是否只使用可以由查询提供程序翻译的表达式。但我通常认为这对于单元测试来说是"足够好的"。