C# 使用IQueryable的单元测试代码
我被要求为某些功能编写一些单元测试,但坦率地说,我不太确定为这段特定的代码编写单元测试的必要性和有用性。我并没有试图质疑单元测试的必要性或实用性 所讨论的代码非常琐碎,经常被使用。基本上,它是对.Skip()和.Take()扩展方法的包装。在我看来,这些方法作为一个整体的合法性是值得怀疑的 代码基本上是这样的:C# 使用IQueryable的单元测试代码,c#,linq,entity-framework,unit-testing,C#,Linq,Entity Framework,Unit Testing,我被要求为某些功能编写一些单元测试,但坦率地说,我不太确定为这段特定的代码编写单元测试的必要性和有用性。我并没有试图质疑单元测试的必要性或实用性 所讨论的代码非常琐碎,经常被使用。基本上,它是对.Skip()和.Take()扩展方法的包装。在我看来,这些方法作为一个整体的合法性是值得怀疑的 代码基本上是这样的: public IQueryable<T> Page(IQueryable<T> query, int page, int size) { if(query
public IQueryable<T> Page(IQueryable<T> query, int page, int size)
{
if(query == null) throw new ArgumentNullException("query");
if(page < 0) throw new ArgumentOutOfRangeException("page");
if(page < 0) throw new ArgumentOutOfRangeException("size");
return query.Skip(page * size).Take(size);
}
publicIQueryable页面(IQueryable查询,int页面,int大小)
{
如果(query==null)抛出新的ArgumentNullException(“query”);
如果(第<0页)抛出新ArgumentOutOfRangeException(“第页”);
如果(第<0页)抛出新ArgumentOutOfRangeException(“大小”);
返回查询。跳过(页面*大小)。获取(大小);
}
当然,我可以对预期的异常进行单元测试,但还有什么?很可能是我没有抓住要点,那么这是怎么回事呢?事实上,因为
IQueryable
可能封装了一个底层的数据存储—这是实体框架的可查询文件的情况—我们谈论的是集成测试
在我的例子中,如果我需要测试像您的代码块这样的东西,我会在数据库中存储一些测试数据,以便以后查询
因为我可以预期结果,所以我可以执行断言来检查被测试的方法是否返回了预期结果
更新
由于我看到了一些与否决票和一些评论相混淆的地方,我想指出,我在回答这个问题时考虑到了这样一个事实,答案被标记为实体框架,我猜可查询项是从
DbSet
中获得的,您可以通过调用
List dummyData=new List();
//添加数据
var result=Page(dummyData.AsQueryable(),0,10);
//对结果执行断言。
如果您实际上只是想测试分页是否正常工作。您可以在这里测试很多东西:
Skip
Take
Take
之前是否调用了Skip
这种测试称为“基于交互的测试” 您还可以使用“基于状态的测试”,方法是使用包含数据的列表调用测试中的方法,并检查返回的数据是否是正确的子集 第2点的测试可能如下所示:
public void PageSkipsThePagesBeforeTheRequestedPage()
{
var sut = new YourClass();
var queryable = Substitute.For<IQueryable<int>>();
sut.Page(queryable, 10, 50);
queryable.Received().Skip(500);
}
public void pageskipsthepages在此之前请求页面()
{
var sut=newyourclass();
var queryable=Substitute.For();
sut.Page(可查询,10,50);
queryable.Received().Skip(500);
}
此测试使用模拟框架。我确信,非常需要测试此方法。 首先,由于缺陷:
if(page < 0) throw new ArgumentOutOfRangeException("page");
if(page < 0) throw new ArgumentOutOfRangeException("size");
如果(第<0页)抛出新ArgumentOutOfRangeException(“第页”);
如果(第<0页)抛出新ArgumentOutOfRangeException(“大小”);
实际上,您没有检查大小变量
无论如何,不需要执行“集成”测试-您可以简单地模拟IQueryable对象并执行单元测试。(见以下答案:)
在我看来,测试这个方法是绝对正确的。您可以编写一个测试,通过传递一个
IQueryable
来确保它的执行,如果迭代而不是迭代结果,它将抛出。1。第三个if
语句有问题。2.请看Jon Skeet的精彩EduLINQ项目,以及他在重新实现LINQ扩展方法的过程中编写的测试。@fuaaark您能否澄清一下,您是否被要求测试这个连接到基础数据库的方法,还是真正的单元测试方法?第三个if语句是否不正确?如果(大小<0)抛出新ArgumentOutOfRangeException(“大小”)代码>你说得对,顺便说一下,这不是实际的代码。这和很多好主意没什么不同,在我的回答中,我考虑过创建测试数据。在您的情况下,这是一个实际的单元测试,而不是我的,它是一个集成测试。这里不需要集成测试。您只需使用在内存中工作的IQueryable
实现。我正要写类似的评论。@DanielHilgarth如果你对我的答案投了反对票,你应该再检查一遍。我不是说“做一个集成测试”。我说过OP的代码是一个集成测试,而不是一个单元测试……………@MatíasFidemraizer:我没有投反对票。但是你的评论是不正确的。1) OP没有显示任何测试代码,因此您无法知道它是什么类型的测试。他展示的代码是一个简单的方法。当然不是集成测试。2) 他展示的方法不需要集成测试。@DanielHilgarth检查OP问题中的[EntityFramework]
标记。也许我疯了,但当我阅读OP的代码并检查标记为实体框架的代码时,我怀疑可查询项来自DbSet
!好的,听起来不错。我不清楚的是,当在其他代码中使用这段经过测试的功能时,如何以干净的方式验证对它的调用?当方法被调用时,我可以设置一些公共属性,但这感觉很难看…@fuaaark:不,不要设置公共属性。你到底想测试什么?你想测试另一个名为Page
方法的方法吗?是的,因为Page()方法在很多不同的地方使用,所以我只想对它进行一次单元测试。但我需要验证它是否被调用。@fuaaark:你想验证它在所有不同的地方被使用吗?是的,我想是的,怎么办
if(page < 0) throw new ArgumentOutOfRangeException("page");
if(page < 0) throw new ArgumentOutOfRangeException("size");