Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用Linq表达式和Lambdas进行测试的存根代码_C#_Linq_Lambda - Fatal编程技术网

C# 使用Linq表达式和Lambdas进行测试的存根代码

C# 使用Linq表达式和Lambdas进行测试的存根代码,c#,linq,lambda,C#,Linq,Lambda,我的代码中有执行以下操作的查询表达式: repository.Context.AsQueryable<Option>().Where(o => o.Id == id && o.Name == "Something").Select(o => o.Id).ToArray(); repository.Context.AsQueryable()。其中(o=>o.Id==Id&&o.Name==Something”)。选择(o=>o.Id.ToArray();

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

repository.Context.AsQueryable<Option>().Where(o => o.Id == id && o.Name == "Something").Select(o => o.Id).ToArray();
repository.Context.AsQueryable()。其中(o=>o.Id==Id&&o.Name==Something”)。选择(o=>o.Id.ToArray();
如何为上述代码创建存根?好像要做很多工作。我是否可以忽略传递给where和Select方法的内容,然后返回我想要返回的内容


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

简短的回答是:不,因为.Where()和.Select()是扩展方法,不能模拟

较长的答案是:是的,因为
上的
.Where()
.Select()
IQueryable
s上什么也不做,只是向基础查询提供程序表明它们刚刚被调用。因此,您可以从技术上为查询提供程序创建一个存根,并在对其求值之前查看它发生了什么

但简单的答案是:我发现最好的方法是使用一个实际的内存表示,它可以像一个可查询的列表一样工作。然后,测试结果数据,而不是尝试验证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);
var options=new[]{new Option(…)};
Setup(r=>r.Context).Returns(contextMock.Object);
Setup(c=>c.AsQueryable()).Returns(options.AsQueryable());
...
Assert.AreEqual(结果[0],选项[0].Id);

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

作为一种选择,将代码用作依赖项。通过这种方式,您可以在不涉及上下文的情况下对其进行存根。例如:

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_存储库;
公共选项服务(IRepository存储库)
{
_存储库=存储库;
}
public int[]GetOptionsWithName(int-id,字符串名)
{
_repository.Context.AsQueryable()
.Where(o=>o.Id==Id&&o.Name==Name)
.选择(o=>o.Id)
.ToArray();
}
}
公共接口IOptionService
{
int[]GetOptionsWithName(int-id,字符串名称);
}

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

存储库的类型是什么。上下文
?是的,我已经在删除存储库和上下文了。我想问题是我能在哪里划掉。我可以说,我不在乎我在Where方法中通过了什么。一定要这样做:它分离了责任,这很好。但是当您想要测试
GetOptionsWithName()
的行为时,同样的问题仍然会出现。在某些情况下,您必须拥有未经测试的代码,或者想出一种方法来测试LINQ语句。测试行为(例如,代码是否调用存储库方法)是非常有争议的。任何代码更改都可能导致重新编写测试。相反,测试状态并不限制方法获取数据的准确程度。我们只关心结果。例如,明天我们可能会决定添加缓存,测试将继续!这太好了。行为正是您应该测试的——它是测试不应该关心的实现细节。添加缓存可能会改变足够多的行为,以至于您希望更改单元测试以覆盖它,或者,可以说,在完全不同的类中实现缓存。但是我同意您不想测试(例如)是否调用了
.Where()
,因为有多种正确的方法来实现相同的行为。我认为,如果您基于输入和输出数据测试方法,那么您是在测试行为,而不是实现细节。基于输入和输出数据测试方法,实际上称为“基于状态的测试”(请参阅)。我们不知道里面发生了什么,我们也不在乎。我们只需要得到预期的结果。基于行为或交互的测试—当您希望确保该方法调用特定的服务、存储库等时,您知道它的实现,对它的任何更改都会导致测试中的更改。根据我的经验,最好只使用基于状态的测试。